How to Get Started with Your Website Content Security Policy

We teamed up with SiteGround
To bring you up to 65% off web hosting, plus free access to the entire SitePoint Premium library (worth $99). Get SiteGround + SitePoint Premium Now

security padlock

This article is part of a series created in partnership with SiteGround. Thank you for supporting the partners who make SitePoint possible.

The web is based on a “same-origin” policy. Only code at mysite.com can access mysite.com’s data in cookies, localStorage, Ajax requests etc. It is isolated from other domains so any access attempts from evilsite.com will be rejected.

Unfortunately, it’s never that simple. Modern websites are complex and load a variety of third-party components, styles and scripts. A script loaded from another domain runs in the context of the current page and can do whatever it likes. That social networking button could monitor visitors, hijack login cookies, change page content and more. Even if you trust the third-party site, you could become victim to a man-in-the-Middle attack where the script is changed before it reaches you. Alternatively, it could permit users to launch their own Cross Site Scripting attacks (XXS).

By default, browsers implement an anything-goes approach. Fortunately, it’s possible to apply restrictions using a Content Security Policy (CSP) which prevent unexpected security issues. A CSP tells the browser what’s permitted, e.g. run JavaScript at mysite.com but only from files and not inline <script> tags.

Test Your Website

To check whether CSP is implemented on your site, visit observatory.mozilla.org, enter a page URL and hit Scan Me. Those with no CSP protection are likely to score an F (although various other checks are made).

CSP should be considered essential for banks, online stores, social networks and any site which implements user accounts. It’s less necessary if your site doesn’t use third-party scripts, fonts, media, widgets or analytics but can you be sure it never will?

Implementing a Content Security Policy

A Content Security Policy must be added to each page by your developer or web host. It’s defined using a Content-Security-Policy HTTP header set by a server-side language (PHP, Node.js, Ruby etc.) or within the server configuration such as Apache’s .htaccess file, e.g.

# Apply a CSP to all HTML and PHP files
<FilesMatch "\.(html|php)$">
Header set Content-Security-Policy "policy-definition"
</FilesMatch>

(We’ll discuss the “policy-definition” value shortly.)

Server configuration files are practical because they apply the same header to all pages within the sub-folder hierarchy. However, you can also define a policy within the HTML <head> of any page using a meta tag:

<meta http-equiv="Content-Security-Policy" content="policy-definition">

This may be necessary if you don’t have permission to configure the server or require differing policies on each page.

Content Security Policy Definition

Now for the complex part. CSPs define a whitelist of permitted domains and contexts for differing types of content.

Presume you only want to permit scripts loaded from your domain. You could use the following CSP (please don’t do this for real yet — it’s just an example!):

script-src 'self';

You then realise you’re also loading a third-party library from a CDN which can appear on various sub-domains of mycdn.com. A domain wildcard is added to the space-separated list:

script-src 'self' *.mycdn.com;

You then remember some of your scripts run inline on the page — we can define that too:

script-src 'self' *.mycdn.com 'unsafe-inline';

We now have a policy for scripts. However, we’ve not defined other types so all stylesheets, images, fonts, etc. would fail to load. To solve this, we can apply a default policy using default-src which serves as a fallback for any undefined type:

default-src 'self'; script-src 'self' *.mycdn.com 'unsafe-inline';

Note that each content type definition is separated with a semi-colon (;). We can now use this policy in our .htaccess file:

Header set Content-Security-Policy "default-src 'self'; script-src 'self' *.mycdn.com 'unsafe-inline';"

or a page meta tag:

<meta http-equiv="Content-Security-Policy" 
content="default-src 'self'; script-src 'self' *.mycdn.com 'unsafe-inline';">

CSP Directive Reference

The full set of CSP directives:

directive description
default-src the default fallback policy. Typically set to 'self' or 'none' to ensure all other directives must be declared
style-src valid stylesheet sources
script-src valid JavaScript sources
connect-src valid Ajax, WebSocket or EventSource sources for JavaScript data retrieval
form-action valid sources for form action attributes
img-src valid image sources
font-src valid font sources
media-src valid HTML5 audio and video element sources
object-src valid plugin sources for HTML object, embed and applet elements
plugin-types valid MIME types for plugins invoked by object and embed, e.g. application/pdf
frame-src valid frame and iframe sources (now deprecated — use child-src instead)
child-src valid frame and iframe sources
frame-ancestors valid embedding sources for frame, iframe, object, embed and applet elements
sandbox enables a sandbox for the resource in a similar way to the HTML5 iframe sandbox attribute. This has a number of restrictions unique to this directive: 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 an address where the browser can POST reports of policy failures

CSP Sources Reference

The CSP source directives ending -src support the following values. Any number of space-separated values can be used:

source description
'none' prevents loading from any source, e.g. frame-ancestors 'none' stops the page showing any iframe or plugin. The value cannot be followed by other sources
'self' allows loading from sources on the same origin (protocol, domain/IP and port)
https: only allows sources on HTTPS connections
data: permits data: sources, e.g. style-src data: allows base64-encoded images in your stylesheets
* wildcard for any URL
*.domain.com permits sources from any sub-domain of domain.com, i.e. www.domain.com, cdn.domain.com, etc.
exact.domain.com permits sources from exact.domain.com
https://exact.domain.com/ permits HTTPS sources on the given domain
'unsafe-inline' permits inline CSS, scripts, javascript: URIs, and element event handlers such as onclick within the HTML
'unsafe-eval' permits unsafe dynamic code using JavaScript’s eval() function
'nonce-id' permits an inline CSS or script to run if the id matches the nonce attribute value, e.g. script-src 'nonce-abc123' runs inline code within a <script nonce="abc123">...</script> block
'sha256-hash' permits styles or scripts if the file content matches the generated SHA-256 hash value

CSP Development Recommendations

It’s practical to start with a strict default policy of default-src 'none'; then add further permissions as required. A good starting point for the majority of websites could be:

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

This permits styles, images, scripts and Ajax requests from the same origin.

Open your page in a web browser then launch the developer tools console. Blocked resource warnings will be reported, e.g.

Refused to load the script 'XXX' because it violates the following Content Security Policy directive: "YYY".

You may need to browse various pages to ensure you’ve accounted for all the fonts, images, videos, scripts, plugins and iframes your site requires.

Google Services

Google provides a great range of services and you’re possibly using analytics, fonts, maps and more. Unfortunately, these are enabled on a range of URIs which require further Ajax calls, inline execution and data schemes. You may end up with a convoluted policy such as:

default-src 'self'; 
style-src 'self' 'unsafe-inline' *.googleapis.com; 
script-src 'self' *.google-analytics.com *.googleapis.com data:; 
connect-src 'self' *.google-analytics.com *.googleapis.com *.gstatic.com data:; 
font-src 'self' *.gstatic.com data:; 
img-src * data:;

(Line breaks have been added for clarity but must not be used in real code.)

This cannot be avoided at the time of writing and other third-party vendors will have similar challenges.

Test Again

Finally, re-test your pages again at observatory.mozilla.org and, with luck, your Content Security Policy grade has improved significantly. The tool will also advise about older browsers, HTTPS, CORS, MIME, cookies, referrer and redirection policy headers.

Implementing a Content Security Policy is an important step in the prevention of unexpected security issues. Another important step is the selection of a hosting provider that takes security to heart. Our partner, SiteGround, is a great option for anyone looking for a web hosting platform built for advanced website security.

We teamed up with SiteGround
To bring you up to 65% off web hosting, plus free access to the entire SitePoint Premium library (worth $99). Get SiteGround + SitePoint Premium Now