The dangers of incorrect CSP implementations

This post examines incorrect CSP implementations on the New Yorker and Blogger and points out common issues in CSP implementations. You will also learn how to fix such problems, check if your CSP implementation is problematic, and test it in Report Only monitoring mode. The article also lists CSP directives and keywords, and demonstrates how to use them correctly.

The dangers of incorrect CSP implementations

Content Security Policy (CSP) is an effective client-side security measure that is designed to prevent vulnerabilities such as Cross-Site Scripting (XSS) and Clickjacking. Following the regular discovery of bypass techniques, a group of researchers led by Google managed to fix these weaknesses in CSP version 3.0. With each new bypass that surfaces, browser developers continue to strengthen CSP.

However, bypasses aren’t the only issue with CSP. Incorrect CSP implementations can also pose critical problems. Keeping in mind that security is not a one time fix but a process, we’re convinced that a significant portion of a secure CSP policy lies in understanding and implementing it correctly.

Incorrect CSP implementations

Useless CSP reports all websites that have incorrectly implemented CSPs. Let’s take a look at some examples.

Incorrect CSP Implementation on The New Yorker

In our first example, let’s look at the CSP header from the HTTP response of The New Yorker of August 31, 2018:

Content-Security-Policy: default-src https: data: 'unsafe-inline' 'unsafe-eval';
child-src https: data: blob:;
connect-src https: data: blob:;
font-src https: data:;
img-src https: blob: data:;
media-src blob: data: https:;
object-src https:;
script-src https: data: blob: 'unsafe-inline' 'unsafe-eval';
style-src https: 'unsafe-inline';
block-all-mixed-content;
upgrade-insecure-requests;
report-uri
https://capture.condenastdigital.com/csp/the-new-yorker

A quick analysis reveals the following:

  • The CSP commands unsafe-inline and unsafe-eval allow inline scripts and scripts from event attributes to execute, something that is highly damaging to the website’s client-site security
  • Really, the only good thing about the header above is that it enforces HTTPS

Incorrect CSP implementation on Blogger

Another incorrectly implemented CSP header reported on Useless CSP was found on Google’s blog service, Blogger:

content-security-policy: script-src   'self' *.google.com *.google-analytics.com 'unsafe-inline' 'unsafe-eval' *.gstatic.com *.googlesyndication.com *.blogger.com   *.googleapis.com uds.googleusercontent.com https://s.ytimg.com https://i18n-cloud.appspot.com   www-onepick-opensocial.googleusercontent.com www-bloggervideo-opensocial.googleusercontent.com www-blogger-opensocial.googleusercontent.com https://www.blogblog.com; 
report-uri /cspreport

Yet again, note the unsafe-inline and unsafe-eval keywords, which effectively disable any script execution restrictions that were put in place by the whitelisting of certain websites. This was an eye-watering discovering, considering Google is among the leading companies promoting the development of CSP.

Lessons from these mistakes

  • These errors demonstrate the fact that everyone makes mistakes, showing how important it is to use an automated web application security scanner which will detect them for you.
  • It’s not always easy to add CSP to an existing website. There are many factors developers need to consider when they need to decide from where external resources should be loaded. This involves caching, available bandwidth and general performance. Security often ranks low on the list of considerations. In order to effectively implement CSP, you either need to consider it before writing the application (the easiest option) or find a way add it on top of your existing applications with the tools CSP provides, most notably nonces and hashes.
  • The problem with these examples are the commands unsafe-inline and unsafe-eval, which remove most of the protections CSP provides against Cross Site Scripting.

How to ensure a good CSP setup

CSP version 3.0 introduced the option to whitelist code blocks using the strict-dynamic directive. Strict-dynamic (covered in detail later in the post) allows some unsafe options such as unsafe-inline and unsafe-eval to be overridden in CSP 3.0. Whitelisting the data: protocol in script-src and default-src directives lays the grounds for attacks such as the following:

www.victim.com/index.php?jsfile=data:,alert(1)
<script src="data:,alert(1);"></script&gt;

You can find more examples on the Useless CSP website.

Common issues in CSP implementations and how to fix them

An incorrect CSP header implementation not only impacts the security of your website but can also affect its operation. Websites today rely heavily on third-party sources. These resources are often loaded from the subdomains of the same website (e.g. static.example.com, scripts.example.com).

The use of inline scripts and JavaScript code in event handlers of HTML elements is quite popular among developers, but that habit isn’t really compatible with CSP. It’s clever to move inline scripts to a subdomain instead, and load them as an external script. Additionally, the host of all the external scripts must be whitelisted in your Content Security Policy, even if you own the subdomains from which the scripts are loaded.

How to check if your CSP implementation is problematic

In practice, there are only three ways to find out whether you’ll have a problem in the implementation of CSP:

  • You could visit every page and check for errors in your browser’s developer console
  • You could wait for the customer complains that your site doesn’t work correctly
  • You could use the CSP Report-Only mode

Naturally, we recommend the third option.

Testing CSP implementations in the Report-Only CSP monitoring mode

But how does the report-only mode work? Before publishing the CSP headers on your website, you can try them in Report-Only mode. In Report-Only mode, the CSP directives are not enforced. Instead the browser reports issues regarding the CSP to an end-point specified in the report-uri attribute. This way, you can find any missing directives, the changes you need to implement and the links you have to whitelist in the CSP header – before you enforce these rules. In addition, it helps you to detect inline JavaScript codes and styles and move them into their respective external file.

Here is an example:

Content-Security-Policy-Report-Only: script-src 'self'; report-uri /my_amazing_csp_report_parser;

This sample script-src directive exclusively whitelists its own origin. All script loadings, inline scripts and script codes in event attributes coming from any other origin will trigger the CSP to send a notification to the end-point specified in the report-uri attribute.

After the testing is complete and you’re ready to push your CSP commands live, you’ll have to disable the Report-Only mode for them to be effective.

This is what the code would look like:

Content-Security-Policy: script-src 'self'; report-uri /my_amazing_csp_report_parser;

Note that even though some CSP directives can be set using HTML’s meta tags, when in Report-Only mode you cannot do that and have to use an HTTP response header instead. This screenshot shows how Netsparker reports ineffective Report-Only CSP directives in an HTML meta tag.

Testing CSP Implementation with the Report-Only CSP Monitoring Mode

Content Security Policy directives

In addition to the CSP header, Content Security Policy has many directives that allow you to configure the security of your websites.

This table lists and explains the directives that can be used to further limit and define the use of resources.

DirectiveDescription
base-uri:The base HTML element contains the absolute URL that is prepended to all the relative URLs on the page. This directive helps us restrict the URLs that are allowed to be used in the base HTML element, and therefore prevent Base Tag Hijacking attacks.
child-src:This directive allows us to define which websites are permitted to be loaded in frames located on the page. We can use it as an extra precaution to protect our page from Frame Injection attacks.
connect-src:This directive restricts the resources that can be loaded via script interfaces such as XHR or WebSockets. This prevents attackers from stealing data from the site.
font-src:This directive specifies the font sources that can be loaded using @font-face. It is mostly used to prevent attackers from sending extracted data back to their server using the @font-face src directive.
form-action:This directive specifies the URLs that can be used as targets for form submissions. It can be used as an extra precaution to protect pages from Form Tag Hijacking and Cross-Site Scripting attacks.
frame-ancestors:This directive specifies the sites that have the authority to load the current page in a frame, iframe, object, embed, and applet tag. It is a substitute for X-Frame-Options, since it can also help prevent Clickjacking and UI Redressing attacks.
img-src:This directive defines the sources from which images can be loaded.
media-src:This directive defines or restricts the sources from which video and audio can be loaded.
object-src:This directive defines or restricts the sources from <object>, <embed>, and <applet>, which helps preventing Cross-Site Scripting attacks.
plugin-types:This directive defines or restricts the plugin types that can be loaded.
report-uri:This directive specifies the URLs that will receive the report when a CSP directive is violated.
style-src:This directive defines or restricts the sources for CSS files. This allows you to avoid data exfiltration via CSS.
upgrade-insecure-requests:This directive converts the HTTP requests to HTTPS.

Examples of using CSP directives correctly

Here are a few examples of how to use CSP directives effectively.

default-src directive example

By default, these directives are unrestrictive, meaning that if they are not declared in the CSP header, any request will be allowed through. So, if style-src is not set, this will be interpreted as style-src: * , allowing styles from all sources.

You can use the default-src directive to change this. The specified value will override most directives ending with -src by setting a default value for them. If you set default-src to http://www.example.com, and don’t set a value for font-src, the fonts can only be loaded from http://www.example.com.

However, default-src cannot override these directives:

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • Sandbox

This is how Netsparker reminds you that default-src does not affect certain directives.

default-src Directive Example

object-src to block plugins

We stated above that CSP is a major mechanism in client-based security against various vulnerabilities like XSS. XSS attacks are generally carried out by script executions, so it would seem that limiting the script and style resources as a precaution might be the best way to mitigate against it. However, there are other HTML elements that can run JavaScript code, such as <embed>, <object> and <applet>.

This is how Netsparker shows that missing the object-src directive in CSP can lead to XSS.

object-src to Block Plugins

You might not want to set the default-src since it’s a fallback mechanism and for the reasons stated above. You’ll probably never want to set it to none. However, you might want to block plugin loadings. In this case you can set object-src to none regardless of whatever you set for default-src.

Example:

Content-Security-Policy: default-src 'self'; object-src 'none';

This option will block plugins from loading on your webpage.

The Keywords That Shape the CSP Directives

Aside from specifying origins from which resources can be loaded, CSP also offers you a few keywords that allow you to further refine your CSP.

KeywordsDescription
‘none’:As the name suggests, nothing is allowed towill be embedded. For example, the object-src: ‘none’ command will not embed any objects on the page.
‘self’:This matches the origin of the current webpage. Resources from other origins including subdomains will not be loaded.
‘unsafe-inline’:This allows the use of inline JavaScript and CSS.
‘unsafe-eval’:This allows the use of text-to-JavaScript functions like eval().

Setting CSP in meta tags

Even though CSP is mostly used to define the directives in HTTP responses, you can set CSP in meta tags, too. This is ideal for situations in which you can’t set HTTP response headers:

<meta http-equiv="Content-Security-Policy" content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'">

Unfortunately the following directives cannot be used when setting CSP between meta tags:

frame-ancestors, sandbox, report-uri
Setting CSP in Meta Tags

Bypassing eval function restrictions

Use of the functions eval, new Function(), setTimeOut and setInterval, which run the text inputs within the document context, is automatically blocked by CSP. To mitigate this, you must make a few changes to the code:

  • If the JSON parsing is carried out using the eval function, you should use the JSON.parse function
  • The strings used in setTimeout or setInterval functions have to be changed to inline functions:

Instead of:

setTimeout("document.querySelector('a').style.display = 'none';", 10);

use:

setTimeout(() => document.querySelector('a').style.display = 'none', 10);
  • If your template system uses generic functions such as new Function(), you can use a system that supports CSP out-of-the-box, such as Angular. You can also use pre-compilation if your template system supports it.

You can use the unsafe-eval keyword if you really have to use text-to-Javascript functions like eval. However, be warned that you’ll be creating a huge security gap in the CSP mechanism.

script-src: 'unsafe-eval'

Benefits of reorganizing your code

An origin-based limitation mechanism could have solved a lot of problems. However, even if an origin-based mechanism was implemented, there’s would still be a large void in the XSS side, Inline Injection:

<script>alert(123);</script>
<a href= onclick="javascript:alert(123)">Click me!</a>
<img src=1 onerror="alert(1);"/>

CSP fixes this problem by blocking inline scripts. Not only does CSP block the codes found between the script codes, but it also blocks the script in event attributes and javascript: URLs.

Therefore you should reorganize the code within the script tags as external files on your website. Doing so has a few benefits:

  • Having the external files cached by the browser will improve the website performance
  • The code will be cleaner
  • Since you will need to minimize the JavaScript code in order to allow fast loading times, this will also make it slightly harder for attackers to find out what it does and how to exploit it

If you still insist on using inline Javascript and CSS, you must specify it within the appropriate directives:

script-src: 'unsafe-inline'; style-src: 'unsafe-inline'

Should you use nonce or hack in the whitelist?

When using CSP to whitelist script or style sources, you’re not limited to origin-based whitelisting the safe resources. You can also use nonce or hash functions to whitelist code blocks.

Let’s assume  you have a code block as below:

<script nonce=EDNnf03nceIOfn39fn3e9h3sdfa>
 //Some inline code I cant remove yet, but need to asap.
</script>

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

You can whitelist the entire code block with a nonce value. As a security measure, for each request, a new nonce value has to be generated, it cannot be reused and has to be random and long.

On the other hand, while using hash instead of a nonce attribute, you can obtain a hash for the script and whitelist it in the respective header.

<script>
alert('Hello, world.');
</script>

Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

CSP supports SHA256, SHA384, and SHA512 hash algorithms.

Deprecated CSP directives

Like any other technology, CSP has developed over time and some directives have been deprecated. You should know that the X-Content-Security-Policy and X-Webkit-CSP directives have been deprecated. Instead you can use the Content-Security-Policy directive.

Security research on CSP 3.0

A few years ago, researchers from Google released CSP is Dead, Long Live CSP, a risk analysis report on frequently used CSP headers. The research was one of the most comprehensive of its kind, covering 1,687,000 hostnames and 26,000 CSP HTTP headers. It also analyzed the three popular methods used to bypass CSP. These are Open Redirection, Insecure JSONP Endpoint, and AngularJS CSP Compatibility Mode.

Insecure endpoint vulnerabilities due to permissive CSP configurations

The report stated that in the script loadings, 14 out of 15 whitelisted domains have insecure endpoint vulnerabilities and attacks targeting them will deactivate the CSP policies.

Here are a few examples of the famous CSP bypassing methods listed in the research.

Bypassing CSP path restrictions:

Open Redirection:

Content-Security-Policy: script-src example.org partially-trusted.org/foo/bar.js

// Allows loading of untrusted resources via:
<script src="//example.org?redirect=partially-trusted.org/evil/script.js">

XSS CSP whitelist bypasses

Insecure JSONP Endpoint

<script src="/api/jsonp?callback=evil"></script>

AngularJS CSP Compatibility Mode

<script src="angular.js"></script>

<div ng-app>{{ executeEvilCodeInUnsafeSandbox() }} </div>

Using nonce-based CSP for dynamic uploads

Google suggests taking control of CSP policies by using nonce-based policies with dynamic uploads instead of whitelist-based policies. Google started supporting the strict-dynamic method to implement the policy it suggests to users on its own browsers. Let’s take a closer view at how this method works.

You’ll load a script over example.com/map.js. So you specify a CSP for it:

Content-Security-Policy: script-src example.com;

You trust example.com, but you also use the unsafe-inline directive to avoid the objects loaded to DOM from being stuck at the CSP barrier – a small concession for you but a large weakness for the attackers.

Content-Security-Policy: script-src example.com 'unsafe-inline';

Besides, by whitelisting the example.com domain in the beginning, we cracked the door open for CSP bypasses from various attack parameters on example.com. Now let’s assume that there’s an open redirection in example.com (note this example below only works in older browsers):

example.com/redirectme.php?go=http://attacker.com/bad.js

If you used nonce-based CSP instead, you would’ve gotten rid of any potential attack vector:

Content-Security-Policy: script-src 'nonce-random-123' 'strict-dynamic';

<script src="http://example.com/map.js" nonce=random-123></script>

The above only trusts the script from example.com/map.js by assigning a nonce value to the code block where the script is loaded. By using strict-dynamic, you allow DOM manipulations to be made from this block and even allow scripts to be loaded by the whitelisted JavaScript code. By doing so, you didn’t have to whitelist the entire example.com domain nor use unsafe-inline or unsafe-eval for the loading of dynamic resources, which protects your website from various attacks.

Backward compatibility with earlier versions of Content Security Policy

The attribute nonce has been supported since CSP 2.0, and strict-dynamic was introduced in CSP 3.0. So what should you do if the user’s browser does not support CSP 2.0 and you’re using nonce? One option is to use the unsafe-inline directive alongside nonce as a backward compatibility method, as shown:

Content-Security-Policy: script-src 'nonce-B92E8649B6CF4886241A3E0825BD36A262B24933' 'unsafe-inline'
<script nonce="B92E8649B6CF4886241A3E0825BD36A262B24933">
console.log("code works");
</script>

When nonce is present, the unsafe-inline command is ignored by the browser. So in browsers that support CSP 2.0 and above, the unsafe-inline command will not be taken into consideration. In browsers where nonce isn’t supported (CSP 1.0), unsafe-inline will be put to work and your page will continue functioning. The backward compatibility implementation for strict-dynamic is as follows:

Content-Security-Policy: script-src 'nonce-B0A48531D5C5EB3F8503430E6D75C83E23B7AE36' 'strict-dynamic' https: http:

With the use of strict-dynamic, the browsers that support CSP 3.0 and above will also ignore the https: and http: commands.

Conclusion

Content Security Policy is an extensive security measure. With the release of new versions and the discovery of new attack patterns, CSP is evolving. Independent research reveals the dangers of a incorrectly implemented CSP header. Therefore, implementing the correct modification is crucial to ensure the safety and functionality of our websites. This is why we recommend that you scan your web application with the Netsparker web security solution, which checks your CSP configuration and alerts you if it detects an unsafe implementation.

Article written by Netsparker security researchers:

Ziyahan Albeniz
Umran Yildirimkaya
Sven Morgenroth

Sven Morgenroth

About the Author

Sven Morgenroth - Senior Security Engineer