Intro to Content Security Policy


1. Introduction

Content security policy (CSP) is an extra layer of security that helps to protect websites from cross-site scripting (XSS). The core idea of CSP to describe the rules that describe which resources are allowed on the web page. Using CSP in combination with traditional methods such as input sanitization and output encoding is a great way to enhance the security of a website.

W3 Web Application Security Working Group governs the standard and version 2 is currently the most widespread with a vast majority of browsers supporting it.

2. Enabling Content Security Policy

We can introduce CSP on a webpage in 2 ways:

  • specifying Content-Security-Policy response header
  • specifying meta tag in HTML document

2.1. Specifying Response Header

To enable CSP from the server-side, we need to add Content-Security-Policy response header. The browser sees the header and loads resources only from the permitted sources. For example, a rule can say that we allow loading scripts from the current domain:

Content-Security-Policy: script-src 'self'

Adding a script from a different domain does not succeed. For example, the browser would not render the following Google Analytics script:

<script src='https://www.google-analytics.com/analytics.js'></script>

Google Analytics is not rendered

To allow loading images only from Wikimedia Commons, we could introduce an extra rule separated by a semicolon:

Content-Security-Policy: script-src 'self'; img-src: 'upload.wikimedia.org'

When adding two images to the webpage: one from a permitted domain and one from a different one, then the first image would be visible:

<img src="https://upload.wikimedia.org/wikipedia/commons/2/27/Hacker-1_%281%29.jpg"/>

<img src="https://images.unsplash.com/photo-1539657523674-fbd149b04c13?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80"/>

2.2. Specifying meta Tag

We can introduce CSP via HTML meta tag.

In case of using HTML tags, the tag has to be placed as early to the document as possible. All the resources before the tag are loaded and parsed without any restrictions. Hence, there’s a possibility that the CSP meta-tag might be tampered by a resource that appears in the HTML document earlier than the CSP tag itself.

3. Executing Inline Scripts

It’s quite common to add inline scripts, such as Google Analytics to a website. By default, executing inline-scripts in a website is forbidden. To enable it, we need to specify rule unsafe-inline:

script-src 'unsafe-inline'

However, we have a problem now. We have allowed executing any inline script, while we only want to run just the Googe Analytics script. How to fix the issue?

3.1 Using nonce-source Policy

The first option would be to use nonce-source policy.

We need to specify the following policy in the headers:

script-src 'nonce-hQPT4OcC0R/48p8l3QLLkw=='

To allow executing specific scripts, we need to add nonce attribute to the script want to run. If the specified attribute value matches with the nonce value in the headers, then the script is executed.

<script nonce="hQPT4OcC0R/48p8l3QLLkw==">
 alert('Hello, World!');
</script>

If the nonce in the headers is not matching with the nonce attribute, then the script won’t be executed. It prevents malicious parties from executing their scripts on the website.

The critical point in this approach is to generate a random nonce every time we serve the page to a client. In case we keep the value of nonce the same, we expose our website once again to XSS.

3.2 Using Hash-based Policy

Hash-based policy is an alternative for nonce-based one.

The approach allows us to whitelist specific inline scripts that we would like to execute. For example, if we would like to add an inline script to our website:

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

We would generate either SHA256, SHA384, or SHA512 hash of the script with base64 encoding:

echo -n "alert('Hello, world.');" | openssl dgst -sha256 -binary | openssl enc -base64

The output is an unique representation of the allowed inline script. To enable it, we need to specify a CSP directive:

script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng='

This directive allows us to specify the exact inline scripts we allow running.

4. Reporting

Even though CSP can save us from security holes, it poses a problem. What if we accidentally block a valid resource? Our website might not look like it’s supposed to or miss vital functionality. To avoid the problem, we can take advantage of CSP reporting functionality.

The CSP reporting enables us to receive reports on any violations. For enabling reporting, we need to specify CSP directive report-uri:

report-uri '/report-violation'

If a violation occurs, then a POST request is made to the specified URL:

{
  'csp-report': {
    'document-uri': 'http://localhost:3000/',
    referrer: '',
    'violated-directive': 'font-src',
    'effective-directive': 'font-src',
    'original-policy': "default-src 'self'; script-src 'sha256-2iWaz2HO1LXYyykhs81x9Qb/Mcp0RDG25FqpEk86u38='; report-uri /report-violation",
    disposition: 'enforce',
    'blocked-uri': 'data',
    'status-code': 200,
    'script-sample': ''
  }
}

5. Conclusion

All in all, CSP helps us to protect websites from XSS by blocking unwanted resources, such as stylesheets, script files or images. We can enable CSP by either specifying appropriate directives using the HTML meta tag or headers in the HTTP response. The latter is the preferred approach.