Table of Contents

Ubuntu - CSP (Content Security Policy)

The Content-Security-Policy HTTP response header helps you reduce XSS risks on modern browsers by declaring what dynamic resources are allowed to load via a HTTP Header.

NOTE: It is known that having both Content-Security-Policy and X-Content-Security-Policy or X-Webkit-CSP causes unexpected behaviours on certain versions of browsers. Please avoid using deprecated X-* headers.


Directive Reference

The Content-Security-Policy header value is made up of one or more directives (defined below), multiple directives are separated with a semicolon ;

DirectiveExample ValueDescription
default-src'self' cdn.example.comThe default-src is the default policy for loading content such as JavaScript, Images, CSS, Font's, AJAX requests, Frames, HTML5 Media. See the Source List Reference for possible values.
script-src'self' js.example.comDefines valid sources of JavaScript.
style-src'self' css.example.comDefines valid sources of stylesheets.
img-src'self' img.example.comDefines valid sources of images.
connect-src'self'Applies to XMLHttpRequest (AJAX), WebSocket or EventSource. If not allowed the browser emulates a 400 HTTP status code.
font-srcfont.example.comDefines valid sources of fonts.
object-src'self'Defines valid sources of plugins, e.g. <object>, <embed> or <applet>.
media-srcmedia.example.comDefines valid sources of audio and video, e.g. HTML5 <audio>, <video> elements.
frame-src'self'[DEPRECIATED]Defines valid sources for loading frames. child-src is preferred over this deprecated directive.
sandboxallow-forms allow-scriptsEnables a sandbox for the requested resource similar to the iframe sandbox attribute. The sandbox applies a same origin policy, prevents popups, plugins and script execution is blocked. You can keep the sandbox value empty to keep all restrictions in place, or add values: allow-forms allow-same-origin allow-scripts allow-popups, allow-modals, allow-orientation-lock, allow-pointer-lock, allow-presentation, allow-popups-to-escape-sandbox, and allow-top-navigation.
report-uri/some-report-uriInstructs the browser to POST a reports of policy failures to this URI. You can also append -Report-Only to the HTTP header name to instruct the browser to only send reports (does not block anything).
child-src'self'Defines valid sources for web workers and nested browsing contexts loaded using elements such as <frame> and <iframe>.
form-action'self'Defines valid sources that can be used as a HTML <form> action.
frame-ancestors'none'Defines valid sources for embedding the resource using <frame> <iframe> <object> <embed> <applet>. Setting this directive to 'none' should be roughly equivalent to X-Frame-Options: DENY.
plugin-typesapplication/pdfDefines valid MIME types for plugins invoked via <object> and <embed>. To load an <applet> you must specify application/x-java-applet.
base-uri Restricts the URLs that can be used to specify the document base URL.

Source List Reference

All of the directives that end with -src support similar values known as a source list. Multiple source list values can be space separated with the exception of 'none' which should be the only value.

Domain patterns can contain both protocol and ports if you want to be specific: http://*.example.com:8080

Source ValueExampleDescription
*img-src *Wildcard, allows any URL except data: blob: filesystem: schemes.
'none'object-src 'none'Prevents loading resources from any source.
'self'script-src 'self'Allows loading resources from the same origin (same scheme, host and port).
data:img-src 'self' data:Allows resources to be inlined with base64 (e.g. Base64 encoded images).
domain.example.comimg-src domain.example.comAllows loading resources from the specified domain name.
*.example.comimg-src *.example.comAllows loading resources from any subdomain under example.com.
https://cdn.comimg-src https://cdn.comAllows loading resources only over HTTPS matching the given domain.
https:img-src https:Allows loading resources only over HTTPS on any domain. Forces HTTPS.
'unsafe-inline'script-src 'unsafe-inline'Allows inline <script> and <style> and onclick elements. This can be further secured by specifying a hash of the code.
'unsafe-eval'script-src 'unsafe-eval'Allows unsafe dynamic code evaluation such as JavaScript eval().

How it Works

The default-src, as the name suggests, sets the default source list for the remaining directives. If a directive isn't explicitly included in the CSP header, it will fall back to using the values in the default-src list.

All directives follow the same pattern:


Content-Security-Policy Examples

Here a few common scenarios for content security policies:

Allow everything but only from the same origin

At its simplest, we could define a CSP to load resources only from the current domain as follows:

default-src 'self';

Only Allow Scripts from the same origin

script-src 'self';

Allow Same Origin, Google Analytics, and Google AJAX CDN

script-src 'self' www.google-analytics.com ajax.googleapis.com;

Starter Policy

This policy allows images, scripts, AJAX, and CSS from the same origin, and does not allow any other resources to load (eg object, frame, media, etc). It is a good starting point for many sites.

default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';

Mixed Content Policy

In order to prevent mixed content (resources being loaded over http, from a document loaded over https), one can use the value “https:” as a directive value.

Content-Security-Policy: default-src https:; connect-src https:; font-src https: data:; frame-src https:; 
img-src https: data:; media-src https:;  object-src https:; script-src 'unsafe-inline' 'unsafe-eval' https:; 
style-src 'unsafe-inline' https:;

The policy prevents mixed content, allows for scheme “data:” in font-src and img-src, allows for unsafe-inline and unsafe-eval for script-src, and unsafe-inline for style-src.

Mixed Content has two categories: Active and Passive. Passive content consists of “resources which cannot directly interact with or modify other resources on a page: images, fonts, audio, and video for example”, whereas active content is “content which can in some way directly manipulate the resource with which a user is interacting.

An example to block only passive mixed content:

Content-Security-Policy: img-src https: data:; font-src https: data:; media-src https:;

An example to block only active mixed content:

Content-Security-Policy: script-src https:; style-src https:; object-src https:; connect-src https:; frame-src https:; 

Prevent Clickjacking

The established way of preventing clickjacking involves the use of the header X-Frame-Options (see: Clickjacking_Defense_Cheat_Sheet). However, CSP 2.0 has a new directive frame-ancestors.

To prevent all framing of your content use:

Content-Security-Policy: frame-ancestors 'none'

To allow for your site only, use:

Content-Security-Policy: frame-ancestors 'self'

To allow for trusted domain (my-trusty-site.com), do the following:

Content-Security-Policy: frame-ancestors my-trusty-site.com 

WARNING: Not supported in all browsers yet.

Also, keep in mind the following (from the CSP Spec):

The frame-ancestors directive MUST be ignored when monitoring a policy, and when a contained in a policy defined via a meta element.

In other words, this will not work when CSP is in a <meta> tag, and will not work when using Content-Security-Policy-Report-Only.

When a report is generated, the blocked-uri will only have a value if it is the same origin as the page.


Re-factoring inline code

By default CSP disables any unsigned JavaScript code placed inline in the HTML source, such as this:

<script>var foo = "314"<script>

The inline code can be enabled by specifying its SHA256 hash in the CSP header:

Content-Security-Policy: script-src 'sha256-gPMJwWBMWDx0Cm7ZygJKZIU2vZpiYvzUQjl5Rh37hKs='

This particular script's hash can be calculated using the following command:

echo -n 'var foo = "314"' | openssl sha256 -binary | openssl base64

Some browsers (e.g. Chrome) will also display the hash of the script in JavaScript console warning when blocking an unsigned script.

The inline code can be also simply moved to a separate JavaScript file:

<script>var foo = "314"<script>

becomes:

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

with 'app.js' containing the 'var foo = “314”' code.

The inline code restriction also applies to inline event handlers, so that the following construct will be blocked under CSP:

<button id="button1" onclick="doSomething()">

This should be replaced by `addEventListener' calls:

document.getElementById("button1").addEventListener('click', doSomething);

Variable assignment in inline scripts. Rather than do this:

<script>var foo = "314";<script>

Leverage HTML5's custom data attributes by setting the value as follows:

<body data-foo="314”>
   ...
</body>

And access the value by doing:

var itemID = document.body.getAttribute("data-foo”);

Content-Security-Policy Error Messages

In Chrome when a Content Security Policy Script Violation happens you get a message like this one in the Chrome Developer Tools:

Refused to load the script 'script-uri' because it violates the following Content Security Policy directive: "your CSP directive".

In Firefox you might see messages like this in the Web Developer Tools:

Content Security Policy: A violation occurred for a report-only CSP policy ("An attempt to execute inline scripts has been blocked"). The behavior was allowed, and a CSP report was sent.

Server Side Configuration

Any server side programming environment should allow you to send back a custom HTTP response header. You can also use your web server to send back the header.

Apache Content-Security-Policy Header

Add the following to your httpd.conf in your VirtualHost or in an .htaccess file:

Header set Content-Security-Policy "default-src 'self';"

Nginx Content-Security-Policy Header

In your server {} block add:

add_header Content-Security-Policy "default-src 'self';";

You can also append always to the end to ensure that nginx sends the header reguardless of response code.


IIS Content-Security-Policy Header

You can use the HTTP Response Headers GUI in IIS Manager or add the following to your web.config:

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Content-Security-Policy" value="default-src 'self';" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

Other Examples of CSP

Facebook

This is how Facebook implements CSP (line breaks added for readability).

default-src *;
script-src https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net *.google.com 127.0.0.1:* *.spotilocal.com:* 'unsafe-inline' 'unsafe-eval' https://*.akamaihd.net http://*.akamaihd.net *.atlassolutions.com;
style-src * 'unsafe-inline';
connect-src https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.spotilocal.com:* https://*.akamaihd.net wss://*.facebook.com:* ws://*.facebook.com:* http://*.akamaihd.net https://fb.scanandcleanlocal.com:* *.atlassolutions.com http://attachment.fbsbx.com https://attachment.fbsbx.com;

Note how Facebook makes use of wildcards for both subdomains, as well as port numbers in connect-src.


Twitter

This is how Twitter implements CSP.

default-src https:;
connect-src https:;
font-src https: data:;
frame-src https: twitter:;
frame-ancestors https:;
img-src https: data:;
media-src https:;
object-src https:;
script-src 'unsafe-inline' 'unsafe-eval' https:;
style-src 'unsafe-inline' https:;
report-uri https://twitter.com/i/csp_report?a=NVQWGYLXFVZXO2LGOQ%3D%3D%3D%3D%3D%3D&ro=false;

Notice how the directives all contain https:, thus enforcing SSL.


Capturing CSP Violations with report-uri

Any violation of your CSP will be logged to the browser console. Whilst that may be fine when your site is under development, it's not really practical when you deploy your CSP to production.

Instead, you can use the report-uri to log all CSP violations. This directive takes a URL as its value, and makes an HTTP POST request to this URL when a CSP violation is detected. The request body contains a JSON object that is populated with details of the violation.

To illustrate this, suppose we have a CSP as follows:

Content-Security-Policy:    default-src 'self'; 
                            report-uri: https://example.com/csp/report;

This means the browser is only permitted to load resources from our own domain. However, our site uses Google Analytics, so it attempts to load JavaScript from www.google-analytics.com. This violates our CSP, so the following JSON is submitted via a HTTP POST request to our report-uri:

{
    "csp-report": {
        "blocked-uri:" "http://ajax.googleapis.com"
        "document-uri:" "http://example.com/index.html"
        "original-policy": "default-src 'self'; report-uri http://example.com/csp/report"
        "referrer:" ""
        "violated-directive": "default-src 'self'"
    }
}

Content-Security-Policy-Report-Only

If you're thinking of implementing CSP, you can take your CSP for a dry run by using the Content-Security-Policy-Report-Only HTTP header instead of Content-Security-Policy. This works just the same way as the CSP header, but it only reports on violations without actually enforcing the policy by blocking restricted resources. You can even use both headers at the same time, enforcing one policy while monitoring the effect any changes might have in the other.


Test

https://securityheaders.io/

https://report-uri.io/home/tools


References

Want more info on CSP, checkout these links: