Today we look at SvelteKit Content Security Policy. Content Security Policy is a set of meta you can send from your server to visitors’ browsers to help improve security. It is designed to reduce the cross site scripting (XSS) attack surface. At its core, the script directives help the browser identify foreign scripts which might have been injected by a malicious party. However, content security policy covers styles, images and other resources beyond scripts.
We see with scripts we can compute a cryptographic hash of the intended script on the server and send this with the page. By hashing the received script itself and comparing to the list of CSP hashes, the browser can potentially spot injected malicious scripts. We will see hashing is not the only choice and see when you might consider using the alternatives. SvelteKit got a patch in February which lets it automatically compute script hashes and inject a content security policy tag in the page head.
You should only attempt to update content security policy if you are confident you know what you are doing. It is possible to completely stop a site from rendering with the wrong policy.
We will see why and how we can use the SvelteKit generated CSP meta tag to add an HTTP Content Security Policy header to a static site. As well as that, we also look at the configuration for deploying the site with headers to Netlify and Cloudflare Pages. We will use the SvelteKit MDsveX blog starter, though the approach should work well with other sites. This should all get us an A rating on SecurityHeaders.com for the site.
If you want to code along, then clone the SvelteKit MDsveX blog starter and install packages:
We just need to update
svelte.config.js to have it create the CSP
meta for us:
We can set mode to
hash will compute a SHA256 cryptographic hash of all scripts
which SvelteKit generates in building the site. These scripts are later used by visitors’
browsers to sniff out foul play. Hashes are a good choice for static sites. This is because
scripts are fixed on build and will not change until you rebuild the site.
With SSR sites, SvelteKit might generate a different script for each request. To avoid the extra overhead of computing a set of hashes for each request, an alternative is to use a nonce. The nonce is just a randomly generated string. We just add the nonce to each script tag and also include it in the CSP meta. Now the browser just checks the nonce in the script match the one in the meta. For this to work best, we need to generate a new random nonce with each request.
The third option,
auto, simply chooses
hash for prerendered content and
nonce for anything else.
This configuration (above) is a little basic. You might want to be a bit more extensive
configuration. In this case, it makes more sense to extract the configuration to a separate file,
and you can update
svelte.config.js like so:
Here is one possible set of values you might use. Of course this will not match your use case, and you should determine a set of values which are suitable.
csp-directives.mjs — click to expand code.
You will need to build the site to see its magic work:
Now, if you open up the Inspector in your browser dev tools, then you should be able to find a meta tag which includes the content security policy.
This is all good, but when I deployed to Netlify and ran a test using the securityheaders.com site. I was getting nothing back for CSP. For that reason, I tried an alternative approach. An alternative to including CSP in meta tags is to use HTTP headers. Both are valid, though the HTTP header is a stronger approach in most cases . Additionally, using HTTP headers you can add reporting, using a service like Sentry. This gives you a heads-up if users start getting CSP errors in their browser.
Netlify, as well as Cloudflare Pages, let you specify HTTP headers for your static site by you
_headers file in your
static folder. The hosts parse this before deploy and then remove it (so it will not be served). My
idea was to write a node script which we could run after the site is built. That script would
build folder for HTML files and then extract the content
security meta tag and add it to a
_headers entry for the page.
Here is the node script I wrote. If you want to try a similar approach, hopefully it will not be too much work for you to tweak it to suit your own use case.
53 you will see I added some other HTTP headers which securityheaders.com looks for. The
findCspMeta function, starting in line
22 is what does the heavy lifting
for finding meta in the SvelteKit generated output. We also use the
node-html-parser package to parse the DOM efficiently. In lines
40 we add the CSP content to a map with
the page path as the key. Later, we use the map to generate the
/build/_headers file. We write
_headers directly to build, instead of
static since we run this script after the SvelteKit build.
Here is an example of the script output:
To run the script, we just update the
package.json build script:
Redeploying to Netlify and testing with securityheaders.com once more, everything now looks better.
One thing you might notice, though, is that the score is capped at A (A+ is the highest rating).
This is because, for now, we need to include the
directive for styles (see line
This limitation is mentioned in the SvelteKit CSP pull request . The note there says this will not be needed once Svelte Kit moves to using the Web Animations API.
In this post, we have taken a peek at this new SvelteKit Content Security Policy feature. In particular, we have touched on:
- why you might go for CSP hashes instead of nonces;
- a way to extract SvelteKit’s generated CSP meta for each page; and
- how you can serve CSP security HTTP headers on your static SvelteKit site.
You can see the full code for this SvelteKit Content Security Policy post in the Rodney Lab Git Hub repo .
- Content security policy is used to guard against cross-site scripting (XSS) attacks and packet sniffing. By adding a few lines of config to your SvelteKit project, you can have SvelteKit generate content security policy meta tags for you. By default, it will generate SHA256 hashes for prerendered pages and nonces for other content and include this data in a meta tag. That tag is placed in HTML head section for each page.
- Generally, hashes offer a more secure approach. For each script included on the page, we generate a cryptographic hash on the server and transmit this with the page. Then the user browser computes a hash of scripts independently. If the browser hash does not match the server transmitted one, there could have been an attempt by a malicious party to alter the script. The browser will not load it. Hashes work well for static sites, since the hashes only need to be generated once, for all users (as the site is built). For SSR pages, the script can be different for each request. To avoid calculating a hash on each request, we instead generate a random string; a nonce. We include the nonce in script tags and in the page CSP meta. Here the browser checks the two nonce match. For improved security, the server should generate a new random nonce for each request.
- For the moment, you will need to include `unsafe-inline` for style-src CSP in SvelteKit sites. This is different to including `unsafe-inline` for script-src. That said, dropping `unsafe-inline` for styles is optimal. This is because a malicious party might try to manipulate inline CSS. For example, hiding an important warning using CSS. In future versions of SvelteKit, you will not need to set `unsafe-inline for style-src and in the meantime will probably continue to use CSP for the other benefits it brings.`
If you have found this video useful, see links below for further related content on this site. I do hope you learned one new thing from the video. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on Twitter, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.
Just wrote a new post on a way you might transform the SvelteKit generated CSP meta tags to HTTP headers served on your static ❤️ SvelteKit site.— Rodney (@askRodney) June 24, 2022
@Netlify and should work on Cloudflare Pages and others.
Hope you find it useful!#staticsveltehttps://t.co/pl4W7gXilG
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram . Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as Search Engine Optimization among other topics. Also, subscribe to the newsletter to keep up-to-date with our latest projects.