HTTPS is quickly becoming a standard implementation when creating a website or application. Most hosting platforms make it rather simple to host our sites over HTTPS. Github pages even makes it totally free and as simple as a button click. However, without setting up a strict Content Security Policy, you're still leaving your sites vulnerable. Content Security Policies aren't exactly a 'sexy' part of development to learn though and strict ones can break your site. The goal of this article is to help clarify some common issues you may run across when setting up a CSP.
Overview
A Content Security Policy (CSP) is a HTTP header, built for protecting against various site attacks, mainly cross site scripting attacks (XXS). A good CSP is strict as possible, only allowing external JS and CSS scripts from third parties that are needed to run the site. Another good (pretty much essential) practice is to provide a nonce tag or an sha based hash for any inlined JavaScript (I'll explain how to do this a bit later).
All this blocking can quickly become a serious issue however, since any script not listed in a CSP will be blocked and if this particular script is essential to run the site (e.g. a runtime script for a JS library) it'll break the whole page. Testing a CSP on a staging environment before implementing it on production is a must.
How to implement
There are a couple ways to implement a CSP, one is by directly adding it in the meta tag of your index.html
, the other is via server side. Directly adding it to the meta tag is quite convenient, especially for statically hosted sites and/or you only have a few external scripts tags you need allow in the CSP. However, server side gives you the added benefit of being able to dynamically add scripts as you need them and can create allow you to create an extra layer or security.
No matter which direction you choose, there will be a few directives you'll need to consider when adding your scripts.
default-src
: As the name implies, this is the default fall back for any directives that aren't applied in your CSP. A common source for this would be 'self', which would allow only scripts that come from the origin.script-src
: This directive defines what sources are allowed to include their JS. Defining these is one of the more crucial directives to protect your site, but keep in mind this is also the one that will cause the most breaks if not set correctly.style-src
: CSS style sheets from different sources. If there's any library that requires to embed their own CSS then include them here, but 'self' is again a standard choice.font-src
: Using google fonts or other font sources? You'll want to add these here.frame-src
: This directive is for those sources that will embed iFrames into your page. Stripe or Google Maps come to mind here.
There are a host of others out there available, just be sure to check the browser support for them. You can find more from the Mozilla Docs.
There's one more thing, the unsafe-inline
value. You can set this value to allow any source to set inlined resources, like JS or CSS. This may prevent your site from breaking, like an inline React runtime, but this is leaving your site almost as vulnerable as it was without setting up a CSP. Avoid this as much as you can.
Avoid unsafe-inline with hashing
The point of only granting certain sites the ability to add their scripts is to protect you from outsiders wanting to sneak malicious code into your code. The unsafe-inline
rule allows any third party to add inlined JS directly into a script tag. To get full advantage of a CSP you'll need to avoid this property by using either a nonce
or a generated hash following at least the sha256
algorithm.
You may have noticed by now, if you've tried setting any of the script-src
or frame-src
directives that Chrome (assuming you use it) may have generated a hash for you, This is possibly the easiest and most straightforward way to add a hash. However, Chrome may not do this for every script or style attribute that is being inlined.
Another popular way is to use Node.js or a backend language to generate them for you and then add them to the CSP dynamically. If you're server-side rendering (SSR) then doing this shouldn't be too difficult. There are some libraries out there to help you generate a hash or nonce; Node.js has crypto
for example. If you're using AWS to host a static site with CloudFront, then you could use a Lambda function installed with Node or Python to do something similar. I'm unsure of other serverless services, but they may also have something comparable.
Top comments (2)
Here is my result -> tiny.cc/-observatory
As everybody should know by reading your good article CSP is awesome