😕 What is Content Security Policy? #
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.
🔬 What is our Focus? #
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.
⚙️ Configuration #
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
, nonce
or auto
. 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.
Alternative Configuration #
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.
🎬 First Attempt #
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.
📜 Header Script #
Netlify, as well as Cloudflare Pages, let you specify HTTP headers for your static site by you
including a _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 crawl the
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.
In lines 47
– 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 34
– 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:
🗳 Poll #
💯 SvelteKit Content Security Policy: Testing it Out #
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 unsafe-inline
directive
for styles (see line 23
of csp-directives.mjs
).
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.
🙌🏽 SvelteKit Content Security Policy: Wrapup #
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.
Let me know if you have different or cleaner ways of achieving the same results. You can drop a comment below or reach out for a chat on Element as well as Twitter @mention
You can see the full code for this SvelteKit Content Security Policy post in the Rodney Lab Git Hub repo .
🏁 SvelteKit Content Security Policy: Summary #
Can SvelteKit generate content security policy? #
- 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.
What is the difference between CSP nonces and hashes? #
- 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.
Do you need to include unsafe-inline in SvelteKit style-src CSP? #
- 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.`
🙏🏽 SvelteKit Content Security Policy: Feedback #
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
Tested on
@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.