Content Security Policy

| 2 min read

In the previous post I showed how to set up a static web site on AWS. After posting, I noticed that the YouTube videos I embedded in the page wouldn't work. In this post I talk about why this happened and how to fix it.

Content Security Policy

The great strength of the Web is that we can link things from different places together, e.g. we can use an img tag to display an image hosted somewhere else or we can import a javascript library from a CDN using the script tag. So far so good. However, we might not want to allow anyone to link anything from anywhere, especially when it comes to active content like scripts or iframes that could be used for nefarious purposes. Whether or not a browser will allow a piece of content to be embedded in a web page is set by the Content Security Policy (CSP) header.

CSP in AWS

In a traditional set up, the Content-Security-Policy settings are part of the webserver configuration. In a serverless deployment, we don't have a web server - instead we have a CloudFront distribution that exhibits a set of 'behaviours' defined by 'policies'.

By default, a CloudFront distribution does not set CSP headers, meaning you can embed whatever you like in your web page. We might prefer to lock this down somewhat so we don't have to worry about exposing our site visitors to potentially malicious content. Add the setting below to 'Content-Security-Policy' under CloudFront -> Policies -> Response headers -> <POLICY_ID> to prevent anything other than scripts or images hosted on this website domain to be added to the content.

default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self';

This works fine up until the point where we want to embed a YouTube video on a page or use a specific font. Because the browser won't allow anything to be embedded in the page other than scripts and images from the same host, we see the following instead of our YouTube video. And our 'IBM Plex Sans' font has been replaced by the default Times New Roman.

A YouTube video blocked by the browser due to CSP settings

We solve this by whitelisting the domains we want to allow content for. For this site, we want to be able to embed YouTube thumbnail images, some node.js packages, YouTube videos and google fonts. To see what the browser is blocking, right-click on the web page in Chrome and click 'Inspect'. You'll see an angry looking red list of errors like the below:

CSP warnings from Google Chrome

Each error will tell you which domain the blocked content is coming from. Add these domains to the appropriate section of the CSP header in the Policies screen mentioned above. I ended up with the following:

default-src 'none'; img-src 'self' https://i.ytimg.com/; script-src 'self' https://cdn.jsdelivr.net/npm/; style-src 'self' https://fonts.googleapis.com; frame-src https://www.youtube.com/; font-src https://fonts.googleapis.com https://fonts.gstatic.com/

Notice that the special sources 'none' and 'self' are surrounded by single quotes, while domain names are not (if you put the domain names in quotes they will not work!). Multiple sources for the same type of content are separated by spaces. Each content type is separated by a semicolon. The AWS documentation says there's a 1783 character limit for CSP headers.

Conclusion

Content-Security-Policy protects site visitors from malicious content, but if it's set too restrictively it can cause unintended behaviour. If your YouTube videos are not embedding correctly, inspect the page using your browser's developer tools to make sure the content isn't being blocked by policy.