Content Security Policy – How hard can it be?

Content Security Policy

How hard can it be?

Michael Schneider
by Michael Schneider
time to read: 12 minutes


  • The Content Security Policy (CSP) is an important method for protection of web applications
  • Correct implementation is highly challenging
  • Many developers and administrators therefore omit it entirely
  • However, smart implementation is possible – and advisable

The Content Security Policy (CSP) has been a separate checkpoint for our Web Application Penetration Tests since September 2015. One of the first projects so tested is emblematic of the acceptance and implementation of CSP in general. In that project, multiple cross-site scripting (XSS) vulnerabilities were found. Along with strict input validation, we also recommended introduction of a CSP header as an additional measure. The developers implemented this and defined a policy. But the script-src directive continued to permit execution of inline JavaScript. This effectively meant installation of a policy that offered no real protection against XSS attacks. In the end, the CSP header was taken out again. No additional resources were invested in a stricter policy definition or an amendment to the web application.

Introduction of an effective Content Security Policy is a demanding task, one that should be carried out in accordance with web development. This article describes how CSP came about and how it is used in practice.

Emergence and development

The first draft of the Content Security Policy Level 1 was published by the W3C Consortium on 15 November 2012. Browser vendors initially implemented it as an experimental function with the header X-Content-Security-Policy or X-WebKit-CSP.

The goal of CSP is to provide both web developers and web server administrators with a tool for mitigation of injection vulnerabilities. When this kind of policy is defined, the web browser is informed which web application resources will be loaded and processed, such as images, style sheets (CSS) or scripts. Here, CSP should be considered a component of a defense-in-depth strategy rather than a replacement for input validation or coding of output.

The X-Content-Security-Policy header is supported by Mozilla Firefox (since Version 4) and Microsoft Internet Explorer 10, but is already considered obsolete and should no longer be used. The Content-Security-Policy header stands for CSP Level 1 and all further levels. It has been supported since browser versions Google Chrome 25, Mozilla Firefox 23, Safari 7 and Internet Explorer Edge.

Through directives, the policy can be tailored to the intended use of the web application. A directive is an instruction to a web browser how to handle several resource types. The default-src directive is considered the standard for all other directives where they are not specified. Directives exist for management of JavaScript, images, objects and style sheets (CSS). Each directive is defined by a source expression. Valid values include none, self, data: and one or more hosts/domains. The Content Security Policy Reference offers a good overview.

In the case of the directive for JavaScript named script-src, it became apparent that the existing source expressions were insufficient for real-life scenarios and thus inhibited implementation of CSP. With Level 2, published on 21 July 2015, the attribute nonce was introduced to ensure additional flexibility. The following section discusses its use – and shortcomings.

The frame-ancestors directive was also introduced with the aim of replacing the HTTP header X-Frame-Options. Here, the expression none equates to the value DENY in the HTTP header. Veit Hailperin already explained this header in his article Inglorious Headers. For now we recommend a dual use strategy, set the policy and the header for it self because not all browsers support this CSP level.

The nonce attribute has not caught on in practice. It was the inclusion of JavaScript libraries from third-party sites that proved particularly problematic. The introduction of the source expression strict-dynamic in the Working Draft for the CSP Level 3 dated 21 June 2016 attempts to solve this. As a further change, the directive report-uri was renamed report-to.

Use in conjunction with JavaScript

Where CSP has not previously been used, we recommend starting with the following directive:

default-src “none”; script-src “self”; connect-src “self”; img-src “self”; style-src “self”;

This will allow the use of images, styles (CSS) and JavaScript from your own domain. When using JavaScript, however, this also means that only integrated script files are executed. Any in-line instructions in script tags are not permitted. For web applications developed in this way, this leads to the problem that the script element is processed directly in the HTML code.

To avoid recoding the entire website, the directive is usually expanded to include the expression unsafe-inline. This means that the web application continues to function as usual, but is once again vulnerable to reflected or stored XSS attacks. This removes the benefit, or protective effect, of the policy.

In many web applications, the use of JavaScript is not restricted to the owner“s domain. External libraries are also integrated into the context of the owner“s page. This means that a whitelist of such domains has to be maintained in the script-src directive. Maintenance is no easy matter and in a worst-case scenario can even lead to a CSP bypass. This is vividly illustrated in solutions for the mini-challenge by Cure53 entitled “H5SC Minichallenge 3: Sh*t, its CSP!.

With the introduction of the attribute nonce in CSP Level 2, there is now the option of use of embedded script elements. The principle behind nonce is that a random nonce value is recorded in the script-src directive. Ideally, this occurs through the web application itself. Every script element that sets this value in the nonce attribute is then authorized. This means that legitimate script elements are executed, but XSS attacks are stopped.

The use of libraries, including JavaScript CDNs like jQuery, React.js and AngularJS, in conjunction with nonce leads to a new problem as described below. The nonce attribute is defined as:

Content-Security-Policy: script-src “nonce-i7bGtfs”

A library is then integrated into the website and correctly provided with the matching value in the nonce attribute:

<script src=“” nonce=“i7bGtfs”></script>

Because the correct value for nonce is set, the integration of the library is permitted. But if further libraries are reloaded as dependencies in the library itself or a script element is used, this leads to an error since the value for nonce is not set there. The second case relates to parser-inserted script elements. A simple example of a parser-inserted element looks like this:

var scriptPath = “”
document.write(“<script src=” + scriptPath + “></script>”);

With the expression strict-dynamic, CSP Level 3 offers a solution for this problem and simplifies the use of external libraries. Using strict-dynamic in conjunction with nonce results in two changes, or effects:

  1. The whitelists defined in the directives default-src or script-src, as well as the use of unsafe-inline, are rejected.
  2. Reloading of dependencies in integrated libraries and script elements that are not parser-inserted are now permitted.

For practical purposes, this offers two benefits. The first effect permits a backwards-compatible rollout of strict-dynamic. The policy

Content-Security-Policy: “unsafe-inline” https: “nonce-abcdefg” “strict-dynamic”

becomes "unsafe-inline" https: in browsers that support CSP Level 1. The https: "nonce-abcdefg" part applies in the case of Level 2 support and "nonce-abcdefg" "strict-dynamic" applies in browsers that have implemented CSP Level 3. The second effect entrusts the integrated script with the integration of its own dependencies without the need to list them specifically in the whitelist.

One worthwhile presentation on CSP bypasses and the introduction of strict-dynamic is the talk by Lukas Weichselbaum and Michele Spagnuolo, both information security engineers at Google, entitled Breaking Bad CSP! at this year“s Area41 2016. The slides from the talk are also available.


The Content-Security-Policy header instructs the web browser to enforce the defined directives. This can, and experience shows that it does, lead to problems with the introduction of CSP or in the development phase of web applications. Therefore, a second CSP header called Content-Security-Policy-Report-Only reports infringements of the directive without blocking them. The web browser sends a report to the URL defined in the directive report-uri or, in future, report-to.

The report contains the following elements:

These reports can be evaluated in respect to overly strict directives, infringements of the directive caused by programming errors or flaws in the web application, as well as potential XSS attacks. The IT security researcher Scott Helme offers a free service for collating and evaluating CSP reports at the website This website is suitable for a first attempt with CSP without the need to build a reporting infrastructure, or with publicly accessible websites that do not contain sensitive data. For sensitive websites, the report service should be operated by a system under your own control.

How hard can it be?

With strict-dynamic, Content Security Policy Level 3 provides a function that offers sufficient flexibility for productive use. The reporting function also offers a reasonable estimation of the consequences of implementation of CSP. This function means that you can test, step by step, how individual directives are configured, so that they offer the greatest level of restriction without hindering the functionality of the website. Web development itself ideally should conform to a policy and essentially refrain from use of inline functions for CSS or JavaScript. Since 2012, continuous work has been carried out on CSP to improve its functionality and simplify its implementation. CSP should no longer be seen as a bogeyman that endangers website operations, but rather as another layer in a secure web application. We would certainly prefer to be able to analyze more CSP headers in the future, instead of simply entering the CSP header is not set in the checklist.

About the Author

Michael Schneider

Michael Schneider has been in IT since 2000. Since 2010 he is focused on information security. He is an expert at penetration testing, hardening and the detection of vulnerabilities in operating systems. He is well-known for a variety of tools written in PowerShell to find, exploit, and mitigate weaknesses. (ORCID 0000-0003-0772-9761)


You want to test the security of your firewall?

Our experts will get in contact with you!

Area41 2024

Area41 2024 - A Recap

Michael Schneider

Reporting and Documenting

Reporting and Documenting

Michael Schneider

Introduction of CVSS v4.0

Introduction of CVSS v4.0

Michael Schneider

Rogue Device

Rogue Device

Michael Schneider

You want more?

Further articles available here

You need support in such a project?

Our experts will get in contact with you!

You want more?

Further articles available here