Intro into Content Security Policy

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?

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 .
  • 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://* , , *
  • <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.


Adding nonce or hash

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

  • 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'

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 inline script Google Analytics, and same origin: script-src 'self' unsafe-inline;
  • Allow content from a trusted domain and all its subdomains default-src 'self' *
  • Allow images from any origin in their own content, and from cdn img-src ‘self’ *;

Evaluate your CSP

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