Logo
Published on

Working with CSP and Vite

Authors
  • Name
    Twitter

Recently I needed to add Content Security Policy to a project which uses Vue JS for the front end and Vite for development server, adding this policy will improve the security of it and it’s general good practice.

So what is Content Security Policy basically it’s a way that prevents you project to load scripts, styles and other objects from insecure sources which can be harmful.

You definitely need to check the amazing documentation there you can find explanations and answers to the most common questions regarding the topic.

There a few ways of setting this type of policy to your project:

  • Meta tag in the HTML file — which I opted for
  • Setting it in the frond end — there are npm packages that can help you
  • Adding them through the back end — great way of doing it and there a spatie package that will help you immensely.

Meta tag in the header is probably the easiest way of doing it, it’s simple as

<meta
  http-equiv="Content-Security-Policy"
  content="connect-src 'self' https://api.stripe.com https://maps.googleapis.com"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="frame-src https://js.stripe.com https://hooks.stripe.com https://www.youtube.com/"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="script-src 'self' https://js.stripe.com https://maps.googleapis.com/"
/>

Something important to mention here:

When set this policy with block every other source except the ones that put in, in the case of the example above the only sources from which objects can be loaded!

This is something that I had issues in the beginning when started the implementation it sounds pretty easy at first but it’s not a trivial task it’s specific for every project.

And so I’ve added the tags to the main HTML file, so far so good, but then I realized that when the I use npm run dev I get console errors:

Refused to load the script 'http://127.0.0.1:5173/resources/js/app.ts' because it violates the following Content  Security  Policy  directive: "script-src 'self' https://js.stripe.com https://maps.googleapis.com/". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
Refused to load the script 'http://127.0.0.1:5173/resources/js/app.ts' because it violates the following Content  Security  Policy  directive: "script-src 'self' https://js.stripe.com https://maps.googleapis.com/". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

and was like: “What?” and I was like that for a few more hours searching why this happen and how to fix it, after a long search I finally figured it out it, the problem was in the vite.config.js file:

server: {
host: process.env.VITE_SERVER_HOST,
}

it turned out that this variable is not set and by default Vite was using 127.0.0.1:5173 for the server which led to the mismatch after I figured that out I was able to fix it changing it to:

VITE_SERVER_HOST=localhost
<meta
  http-equiv="Content-Security-Policy"
  content="connect-src 'self' ws://localhost:* https://api.stripe.com https://maps.googleapis.com"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="frame-src https://js.stripe.com https://hooks.stripe.com https://www.youtube.com/"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="script-src 'self' localhost:* https://js.stripe.com https://maps.googleapis.com/"
/>

it worked which is fine but the thing is that, I’m not the only one working on the project and the others could use a different setup which involves using a different values for the Vite server env variable, so I’ve changed it to:

<meta
  http-equiv="Content-Security-Policy"
  content="connect-src 'self' ws://{{ env('VITE_SERVER_HOST') }}:* https://api.stripe.com https://maps.googleapis.com"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="frame-src https://js.stripe.com https://hooks.stripe.com https://www.youtube.com/"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="script-src 'self' {{ env('VITE_SERVER_HOST') }}:* https://js.stripe.com https://maps.googleapis.com/"
/>

this way it will add the proper server address depending on the value set in the environment file. And since this is only needed when developing, ultimately I’ve decided to go with the following:

@if(App::environment() == 'production')
<meta
  http-equiv="Content-Security-Policy"
  content="connect-src 'self' https://api.stripe.com https://maps.googleapis.com"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="frame-src https://js.stripe.com https://hooks.stripe.com https://www.youtube.com/"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="script-src 'self' https://js.stripe.com https://maps.googleapis.com/"
/>
@else
<meta
  http-equiv="Content-Security-Policy"
  content="connect-src 'self' ws://{{ env('VITE_SERVER_HOST') }}:* https://api.stripe.com https://maps.googleapis.com"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="frame-src https://js.stripe.com https://hooks.stripe.com https://www.youtube.com/"
/>
<meta
  http-equiv="Content-Security-Policy"
  content="script-src 'self' {{ env('VITE_SERVER_HOST') }}:* https://js.stripe.com https://maps.googleapis.com/"
/>
@endif

if the project is in production not to add the Vite server address since it’s part of the development process…yeah I know it’s not pretty but I work.

Conclusion

Content Privacy Policy is a great way to add an additional layer of security to your project, it’s not that hard to add it, if you are stubborn enough.