Intro into Content Security Policy

Image for post
Image for post
Photo by John Tecuceanu on Unsplash

Not long ago, I was tasked to assess our current Content Security Policy (CSP) and find out ways to improve it. It took me longer than I thought, as I need to familiarise myself with the terminologies and protocols before go into implementation detail.

What is CSP?

Content-Security-Policy(CSP) is the name of a HTTP response header (or as meta tag) that browsers use to safeguard your site.

You may ask “aren’t we already have CORS and Same-site policy? Well, attackers have found clever ways to compromise the system. For example, Cross-site scripting (XSS) attack originates from the browser’s inability to distinguish between script from your application and script that’s been maliciously injected by a third-party but faked to be part of your application. With CSP, the server defines a whitelist of content from trusted sources.

How to add CSP?

1. as headerContent-Security-Policy: <directive> <value>; <directive> <value>; <directive> <value>;2. as meta tag<meta http-equiv="Content-Security-Policy" content="<directive> <value>; <directive> <value>; <directive> <value>;">

The CSP mechanism allows multiple policies to be specified, via the Content-Security-Policy header( Content-Security-Policy-Report-Only header) and a <meta> element.

You can even implement multiple policies. But with multiple policies, the CSP take the most strict ones.

Apart from that, the CSP should include a default-src policy directive, which is a fallback for most other resource types.

Possible directives can be:

  • base-uri URLs that can appear in <base> element.
  • child-src URLs allowed for workers and embedded frame contents, e.g. child-src https://video.com .
  • frame-src if not present it still falls back to child-src
  • connect-src origins that you can connect to, e.g. via XHR, WebSockets, and EventSource.
  • font-src origins that can serve web fonts.
  • form-action valid endpoints for submission from <form> tags.
  • frame-ancestors sources that can embed the current page, which applies to <frame>, <iframe>, <embed>, and <applet> tags.
  • img-src origins from which images can be loaded.
  • media-src origins allowed to deliver video and audio.
  • object-src control over Flash and other plugins.
  • plugin-types plugins that a page may invoke.
  • report-uri a URL where a browser will send reports to, unavailable through <meta> tags.
  • style-src origins allowed for stylesheets.

Note that the following directives don’t use default-src as a fallback: base-uri , form-action , frame-ancestors , plugin-types , report-uri , sandbox .

Below are possible values matching with directives:

  • <host-source>: Internet hosts by name or IP address, as well as an optional URL scheme and/or port number: http://*.example.com , example.com:443 , *.example.com.
  • <scheme-source>: A scheme such as http: or https: , data: .
  • 'self': Refers to the origin from which the protected document is being served, including the same URL scheme and port number.
  • 'unsafe-eval' : Allows text-to-JavaScript mechanisms like eval.
  • 'unsafe-hashes': Allows enabling specific inline event handlers.
  • 'unsafe-inline': Allows inline JavaScript and CSS such as inline <script> elements, javascript: URLs, inline event handlers, and inline <style> elements.
  • 'none': Nothing allowed.
  • 'nonce-<base64-value>': A white-list for specific inline scripts using a cryptographic nonce (number used once). The server generates a nonce value each time it transmits a policy.
  • ‘<hash-algorithm>-<base64-value>’: A sha256, sha384 or sha512 hash of scripts or styles.
  • ‘strict-dynamic’: The strict-dynamic source expression is raised in CSP Level 3, and specifies a given <script> element’s contents are authorised to execute if the element includes an attribute nonce whose value matches a nonce given in the HTTP response that served the page. If the nonces do not match (or are not both present) the script will not execute.
  • ‘report-sample’: Requires a sample of the violating code to be included in the violation report.

Inline-script

Adding nonce or hash

Values like unsafe-inline are dangerous, but sometimes are necessary evil. Especially if you require some 3rd party scripts like Google Tag Manager to run on your website. For that, we can use a cryptographic nonce or a hash as mentioned above.

To use a nonce, give your script tag a nonce attribute. Its value must match one in the list of trusted sources. For example:

<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">function(){}</script>

Now, add the nonce to your script-src directive appended to the nonce- keyword.

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

Another way of doing so is with Hash. To use hash,encrypting all characters inside the <script> tag and insert the hash value in the script-src directive, prefixing it with sha256-, sha384-, or sha512-.

<script>function(){};</script>==========> Content-Security-Policy: script-src 'sha256-5ef5d1ac865d5989c69073dafa4c847fa199f1777c72dfb16a5d61104b6bc6f3'

Adopting a strict CSP

To enable a strict CSP policy, most applications will need to make the following changes (we use nonce in our case):

  • Adopt a middleware to generate nonce.
  • Convert attribute javascript event to inline javascript event.
  • Add a nonce attribute to all <script> elements. Some template systems can do this automatically for example Google Closure. But this is the most difficult part as some framework does not allow you to do that easily.
  • Test the change in Report header.

Use ‘strict-dynamic'

Since CSP Level 2, nonces have offered an alternative to whitelisting individual origins. But the biggest problem with the nonce is that dynamically generated scripts added at runtime would fail to execute.

All such scripts would have to be refactored and moved off into defined external scripts so that the nonce could be included during page creation.

CSP Level 3 solves this with the strict-dynamic keyword. This keyword makes it so that dynamically generated scripts inherit the nonce from the trusted script that created it.

The good thing is the backward compatibility. When strict-dynamic is used, browsers that support it will ignore the following source list expressions:

  • ‘unsafe-inline’
  • ‘unsafe-eval’
  • ‘self’
  • http: or https: host based source lists

So for example ‘unsafe-inline’ https: ‘nonce-EDNnf03nceIOfn39fn3e9h3sdfa’ ‘strict-dynamic’ will be ‘unsafe-inline’ https: in CSP1, https: ‘nonce-EDNnf03nceIOfn39fn3e9h3sdfa’in CSP2, and ‘nonce-EDNnf03nceIOfn39fn3e9h3sdfa’ ‘strict-dynamic’ in CSP3.

CSP Examples

  • Allow everything but only from the same origin: default-src 'self';
  • Allow inline script Google Analytics, and same origin: script-src 'self' unsafe-inline www.google-analytics.com;
  • Allow content from a trusted domain and all its subdomains default-src 'self' trusted.com *.trusted.com
  • Allow images from any origin in their own content, and from cdn img-src ‘self’ *.cloudfront.net;
Image for post
Image for post

Evaluate your CSP

There are some useful chrome extensions to help you build or monitor your CSP policy, or you can just copy your CSP and paste it in the validator like https://csper.io/.

That’s today’s content!

Happy Reading!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store