It’s always a good idea to be aware of the security issues that might affect your site. For example, cross-site scripting (XSS) attacks consist of injecting malicious client-side scripts into a website and using the site as a propagation method for other malicious behavior.
XSS attacks are possible because browsers trust all requests that come from your website, inline, or from an external source. This post aims to introduce you to a tool that can help you mitigate some of the risks of XSS attacks: the Content Security Policy.
Mitigating XSS attacks with a Content Security Policy (CSP)
Client-side scripts can be programmed to do pretty much anything. They can be as simple as showing an alert message in your website, to animating images, skimming credit cards, capturing login credentials, or showing pop-ups that contain NSFW pharma products.
It’s important to understand that when attackers find a website vulnerable to XSS attacks, they have two options of injecting malicious scripts into it:
- Inline script: when attackers put their code directly into your website’s HTML.
- Load script from an external domain: when attackers load a script from multiple infected sites and modify the script when they need to. All the changes will be reflected on those sites.
Loading scripts from an external domain is usually the preferred method by hackers. So, let’s dive into some ways to mitigate these attacks by setting up a Content Security Policy (CSP).
What is a Content Security Policy (CSP)?
According to W3’s website, a Content Security Policy is:
A tool which developers can use to lock down their applications in various ways, mitigating the risk of content injection vulnerabilities such as cross-site scripting, and reducing the privilege with which their applications execute
A CSP accomplishes this by allowing web developers to define directives such as:
- Execute all code coming from my domain (e.g awesomedomain.com)
- Execute all code coming from an external trusted domain and subdomains (e.g *.trusteddomain.com)
- Execute nothing except for the script (e.g. awesomedomain.com/script.min.js)
- Do not execute JavaScript
- Show only images coming from a trusted location (e.g. cdn.securecdn.com)
- A mix of the these examples and much, much more!
Firefox will respect the second version of the CSP since version 31, Chrome since version 40, and Safari since version 10. Browsers that don’t support a content security policy will simply ignore directives.
How do you define a CSP?
Every CSP is made of two parts:
- The first part is a set of directives which tell the browsers of your website’s visitors how to control specific resources in your website.
- The second part is a disposition which tells browsers whether to enforce the CSP or not.
We will talk about the disposition of a CSP later in the post.
Let’s see an example of how we can define a policy around JavaScript using the script-src directive.
Content-Security-Policy: script-src 'none'
This directive will prevent the execution of JavaScript in the browsers that respect the Content Security Policy.
Now that we’ve created one directive, let’s stop for a moment and see how we can deliver it to the browser of your website’s visitors. You have two options:
The first option is to use the HTMLtag, which should be placed as early as possible in the HTML of your web pages.
Here’s how you can set the script-src directive to block all JavaScript in a < meta > tag:
<meta http-equiv="Content-Security-Policy" content="script-src 'none';">
The second method is to use a Content-Security-Policy HTTP Response Header.
For example, if you use Apache, you can define the CSP in the httpd.conf, VirtualHost, or .htaccess file of your site.
Just add it like this (same example blocking all JavaScript):
Header set Content-Security-Policy “script-src 'none';”
The second method is considered to be more secure and, in fact, the W3 itself recommends serving CSPs this way.
What are some examples of policies?
First of all, let’s see some of the directives we can define in our policies:
- script-src will restrict the location from which scripts are executed (it will also block inline scripts and the eval() function).
- media-src will restrict the location from which video, audio, and associated text track resources are loaded.
- frame-src will restrict the location from which iFrames are loaded.
- img-src will restrict the location from which images are loaded.
- font-src will restrict the location from which font files are loaded.
- style-src will restrict the location from which .css files are loaded (and also block inline styles).
- default-src will apply a security policy to child-src, connect-src, font-src, frame-src, img-src, manifest-src, media-src, media-src, object-src, script-src, style-src and worker-src where they themselves are undefined (will save you typing!).
You can see the complete list of directives you can set on W3’s website.
Second, let’s see some of the keywords/values you can set your directives to:
- ‘none’ will match nothing – meaning that all elements controlled by the directive will be blocked (see our example blocking all JavaScript in a page).
- ‘self’ will match the current URL’s origin (the URL of your website).
- ‘https://*’ will match all resources loading over HTTPS.
- ‘*.awesomedomain.net’ will match the domain awesomedomain.net and all subdomains
‘https://awesomedomain.net/script.js’ will only match the script script.js in the domain awesomedomain.net over HTTPS.
Let’s now see a few concrete examples.
Example 1:
CSP Allowing only JavaScript hosted in your site:
Content-Security-Policy: script-src 'self'
Example 2:
CSP Allowing only JavaScript and images hosted on your site:
Content-Security-Policy: script-src 'self'; img-src ‘self’;
Example 3:
CSP Allowing only JavaScript hosted on your site and cdn.trustedorigin.net but images hosted everywhere:
Content-Security-Policy: script-src 'self' cdn.trustedorigin.net; img-src *;
Example 4:
CSP blocking iFrames in your site:
Content-Security-Policy: frame-src ‘none;’
Example 5:
CSP blocking all form submissions in your site:
Content-Security-Policy form-action 'none';
Example 6:
CSP only allowing the file script.js on https://trustedorigin.net/ and default the values of child-src, connect-src, font-src, frame-src, img-src, manifest-src, media-src, media-src, object-src, script-src, style-src and worker-src to the current URL’s origin
Content-Security-Policy default-src ‘self’; script-src https://trustedorigin.net/script.js;
How do you test your Content Security Policy?
As we mentioned before, it’s important to know that every policy has a disposition, which is either “enforce” or “report“.
The disposition “enforce” applies the CSP, while the “report” one allows developers to experiment with the policy without actually enforcing it.
Enabling Content Security Policy reports with Content-Security-Policy-Report-Only
You can enable reports for your content security policy by defining the Content-Security-Policy-Report-Only header (instead of the Content-Security-Policy header) and adding the directive report-to with a URL where you would like to see reports about CSP violations.
An example of this header should look something like this:
Content-Security-Policy-Report-Only: default-src https:; report-to /your-csp-report-endpoint/
Once enabled, you’ll get JSON blob from your browser containing any detected violations for the CSP. The idea here is that you might fix any accidental violation to your policy before enforcing it.
Violation reporting isn’t configured by default, and for somewhat good reason. You may end up with a lot of noise and confusing or unactionable report data.
However, the Content Security Policy reports do contain a number of interesting details. Here are some of the objects and related descriptions that may be included in your CSP report:
Object | Description |
blocked-uri | The URI of the resource that was blocked from loading by the Content Security Policy. |
status-code | The HTTP status code of the resource. |
effective-directive | The directive that caused the violation. |
original-policy | The original policy as seen in the Content-Security-Policy-Report-Only HTTP header. |
script-sample | The first 40 characters of the script, event handler, or style that caused the violation. |
referrer | The referrer of the document where the violation occurred. |
document-uri | The URI of the document where the violation occurred. |
Content security policy reports are especially useful if you’re just getting started with CSP and need to troubleshoot issues for your site. Also worth noting that this header is not supported inside a <meta> element.
How do you fix mixed content warnings with a CSP?
Next, let’s take a look at the issue of mixed content warnings. You can address this with a CSP by defining the upgrade-insecure-requests directive which will do all the heavy work for you.
When you use upgrade-insecure-requests in your CSP, all HTTP requests are upgraded to HTTPS by the browser before carrying out network requests.
If in your website you have something like this:
<img src=”http://awesomedomain.net/image.jpg”>
It will be requested as if this were this:
<img src="”https://awesomedomain.net/image.jpg”" />
You can also go one step further and add the following directive to your CSP:
block-all-mixed-content
As the name of the directive implies, it will block all content that is not accessible through HTTPS.
Both the upgrade-insecure-requests and the block-all-mixed-content directives were thought to maintain the security of your site, hence, they will block resources not available over HTTPS. Careful with these directives though, as they can break your site.
Please, remember that browsers not respecting the CSP of your site will still display mixed content warnings on your site. This should not be considered a complete fix for the problem. You can learn more about how to solve the root problem of Mixed Content Warnings in this blog post and our free guide on How to Install an SSL Certificate.
Protecting your website from XSS
XSS attacks can be extremely harmful to an online business. The injection of unwanted malicious scripts can get your website blocklisted by Google or other search authorities, and, consequently, hurt your website’s reputation.
It’s very important to understand that a CSP will not stop attackers from exploiting vulnerabilities in your website. But it does stop modern browsers from executing malicious scripts if they are ever injected into your site. You should consider a content security policy as another layer of protection around your website, not as the only line of defense.
If you are looking for a complete website security solution that will proactively monitor and protect your website, we offer a comprehensive Website Security Platform that can help mitigate a wide range of vulnerabilities and attacks. Chat with us to learn more! We’re available 24/7 to lend a hand.