Skip to content

Aquent | DEV6

Content Security Policy

Written by: Pavlo Leheta

The other day I was working on an Electron project and followed their security recommendations. One of them was to define Content Security Policy and use restrictive rules. So, I got more information about this particular security measure and was going to do an overview of it to familiarize you with this important concept.

What is the Content Security Policy (CSP)?

Content Security Policy or CSP is a new security mechanism supported by modern browsers. It’s a new layer of security which aims to mitigate certain types of web attacks like Cross-Site Scripting (XSS) and data injection which are the result of executing malicious content in the web browser’s trusted context.

What does CSP help to solve?

XSS attacks try to exploit the browser’s trust of content that was received from the server. The victim’s browser executes malicious scripts because it trusts the source of the content and cannot distinguish between the scripts that intended to be a part of the application and scripts maliciously injected by a third-party.

Content Security Policy helps to mitigate and report XSS attacks. It makes it possible to eliminate or reduce ways by which XSS attacks can occur by specifying the domains that the browser should consider as trusted sources of executable scripts. A browser compatible with CSP can then only execute scripts loaded in source files received from the whitelisted domains.

How to enable and use Content Security Policy?

Your web server needs to be configured to return the Content-Security-Policy HTTP header. As an alternative, the element can be used to configure a policy, something like:

    <meta http-equiv=”Content-Security-Policy” content=”<policy here>”>

In order to use CSP, the Content-Security-Policy HTTP header has to be added to a web page and contain values to control which resources the browser is allowed to load for that specific page. Let’s see an example:

     Content-Security-Policy: script-src ‘self’ https://www.dev6.com

Here script-src is a directive which controls a set of script-related privileges for a specific page. The ‘self’ source (the current page’s origin) and https://www.dev6.com are valid, trusted sources of scripts that the user agent will download and execute. So, whenever an attacker would manage to inject malicious code they will see an error in the browser’s console which says:

  • “Refused to load the script ‘http://evil.com/evil.js’ because it violates the following Content Security Policy directive: “script-src ‘self’ https://www.dev6.com”

A policy is described using a series of policy directives for a certain resource type or policy area. It is recommended that default-policy directive included in the policy as it provides a fallback for other types of resources in case they don’t have their own policies. CSP provides a wide range of policy directives which can be used to finely tune the control over the resources the page is allowed to load. The concept is the same as for the script-src directive used in the example above.

Some of the available directives include:

  • default-src sets a default source list for a number of directives, pretty much all that end with -src.
  Content-Security-Policy: default-src 'self'
  • base-uri (does not fall back to the default-sources) restricts the URLs that can appear in page’s <base> element
  • child-src lists the valid sources for web workers and nested browsing contexts such as <frame> and <iframe>
  • connect-src limits the origins to which you can connect like XHR, Web Sockets, EventSource, Fetch
  • font-src specifies the origins that can serve web fonts
  • form-action lists valid endpoints for submission from <form> tags
  • img-src defines the origins from which images can be loaded.
  • media-src restricts the origins allowed to deliver video and audio.
  • style-src is similar as script-src but for stylesheets
  • object-src specifies valid sources for the <object>, <embed>, and <applet> elements.
  • sandbox is a bit different as other directives. It applies restrictions on actions the page can take as opposed to resources the page can load. If this directive applied, the page will be treated as if it was loaded inside an iframe with a sandbox attribute. This can have effects like preventing popups, preventing the execution of plugins and scripts and enforcing the same-origin policy.

These are just some of the CSP directives available. To see the exhaustive list of all directives follow this link and examine the CSP directives section.

Some of the predefined sources that can be used with a directive include:

  • Refers to the origin from which the protected document is being served, including the same URL scheme and port number. You must include the single quotes
    • ‘self’ – the origin from which the protected document is being served, including the same URL scheme and port number.
    • ‘unsafe-inline’ allows the use of inline resources such as <script>, <style>, javascript: URLs and inline event handlers. Single quotes must be included.
    • ‘unsafe-eval’ allows the use of eval() and similar methods for creating code from strings.
    • ‘none’ refers to the empty set, no URLs match.
    • ‘nonce-<base64 value>’ – a whitelist for specific inline scripts using a cryptographic nonce. The server must generate a unique nonce value each time it transmits a policy. When nonce is specified, it makes a modern browser to ignore ‘unsafe-inline’ source.

Let’s take a look at some examples.

A website wishes to load images from any URL, plugin content from a list of trusted media providers (including a content distribution network) and scripts only from a server under their control which hosts sanitized JavaScript:

Content-Security-Policy:

default-src 'self'; img-src *;

object-src media1.example.com media2.example.com *.cdn.example.com;

script-src trustedscripts.example.com

A website that relies on inline script elements wishes to ensure that script is only executed from its own origin, and those elements it intentionally inserted inline:

Content-Security-Policy: script-src 'self' 'nonce-$RANDOM';


The inline script elements would then only execute if they contained a matching nonce attribute:

<script nonce="$RANDOM">...</script>

CSP support and compatibility

According to caniuse.com 89.29% of all users globally have access to browsers that support Content Security Policy Level 2. That includes pretty much all modern browsers used nowadays both desktop and mobile.

Testing CSP

In order to make a deployment easier, a CSP can be deployed in report-only mode. In that case the policy will not be enforced, however any violations are reported to a provided URL. Also, a report-only header can be used to test a future revision to a policy without doing any deployment. That way the policy can be specified like the following:

Content-Security-Policy-Report-Only: policy

Whenever both Content-Security-Policy-Report-Only and Content-Security-Policy headers present in the same response, they both are fulfilled. Only the latter is enforced whereas the former just generates reports.

CSP reporting

Violation reports aren’t sent anywhere by default. This functionality has to be turned on by specifying the report-uripolicy directive and providing at the minimum one URL to which the reports can be delivered like so:

Content-Security-Policy: default-src 'self'; report-uri

http://reportcollector.example.com/collector.cgi

Additionally, the server has to be set up to receive those reports. They could be stored or processed depending on a use case.

Let’s look at a sample violation report. A page located at http://mysite.ca/index.html uses the following CSP policy disallowing everything besides stylesheets from cdn.mysite.ca.

Content-Security-Policy: default-src 'none'; style-src 

cdn.mysite.ca; report-uri /reports/csp-reports

The html looks like this:

<!DOCTYPE html>
<html>
        <head>
              <title>My Site</title>
              <link rel=”stylesheet” href=”css/mystyle.css”>
        </head>
        <body>
              ... some content
        </body>
</html>

Stylesheets are only allowed to be loaded from cdn.mysite.ca and the website tries to load from it’s origin http://mysite.ca

A modern browser which supports CSP will send the following violation report via a POST request to http://mysite.ca/reports/csp-reports upon document visit:

{ 

     "csp-report": {

        "document-uri": "http://mysite.ca/index.html",

        "referrer": "",

        "blocked-uri": "http://mysite.ca/css/mystyle.css",

        "violated-directive": "style-src cdn.mysite.ca",

        "original-policy": "default-src 'none'; style-src

cdn.mysite.ca; report-uri /reports/csp-reports"

    }

}

Summary

To conclude I would like to emphasize that there are never enough security measures that can be implemented on your web site or application to protect it from malicious actions. The Content Security Policy is one of those measures that can be tailored for specific scenarios and helps to mitigate such web attacks as Cross-Site Scripting (XSS) and data injection by letting modern browsers know what resources can be loaded safely.

References

https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

https://www.html5rocks.com/en/tutorials/security/content-security-policy/

https://www.w3.org/TR/CSP2/