Mitigating cross-site scripting with Content Security Policy
Tags: security, xss, javascript, webdev
Published: 2017-12-21 19:18:20

---
title: Mitigating cross-site scripting with Content Security Policy
published: true
description: Learn how to use Content Security policy to defend your site against cross-site scripting attacks
tags: security, xss, javascript, webdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/psysv4rqqxwy5flt54ir.PNG
---

In this post, we're going to look at using Content Security Policy (CSP) as a defense-in-depth technique to block script injection attacks.

When building website that hosts user-generated content, such as:

```html
Great to be here!
<script>window.location='https://example.com'</script>
```

It's necessary to encode user-generated content so that browsers don't mistake it for markup, and execute an untrusted script. This is easy to do for plain text, but what if a page needs to render user-generated HTML? Here's an example of HTML that contains inline Javascript, which browsers could execute:

```html
<p>Great to <b>be</b> here!</p>
<img src="" onerror="alert(0)" />
<a href="javascript:alert(0)">Hi</a>
<script>window.location='https://example.com'</script>
```

This content must be sanitized before rendering. Libraries such as HTMLAgilityPack or DOMPurify provide a way to parse the HTML and strip out elements or attributes known to execute scripts.

Sanitization is important, but what if an attacker has discovered a way around the filter? This is where [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) comes in.

If the `Content-Security-Policy` header is present when retrieving the page, and contains a `script-src` definition, scripts will be blocked unless they match one of the sources specified in the policy. A policy might look something like:

```
script-src 'self'; object-src 'none'; base-uri 'none';
```

This policy disallows:
- External scripts not hosted on the same domain as the current page.
- Inline script elements, such as `<script>`
- Evaluated Javascript, such as `<img src="" onerror="alert(0)" />`
- Base elements, which could break scripts loaded from a relative path
- Object elements, which can host interactive content, such as Flash

### Whitelisting inline scripts

Sometimes it is necessary to run inline scripts on your page. In these cases, the `nonce` attribute on `script` elements can be used to whitelist scripts that you control.

```html
<script nonce="00deadbeef">doSomething()</script>
```

A matching nonce must be present in the CSP for the script to run. For compatibility with older browsers, `unsafe-inline` allows scripts to run if the `nonce` tag is unsupported.

```
script-src 'self' 'nonce-00deadbeef' 'unsafe-inline'; object-src 'none'; base-uri 'none';
```

It is critical that this nonce is derived from a cryptographic random number generator so that an attacker can't guess a future nonce. In .NET, `RNGCryptoServiceProvider.GetBytes` can be used to fill a 16 byte array:

```csharp
using (var random = new RNGCryptoServiceProvider())
{
    byte[] nonce = new byte[16];
    random.GetBytes(nonce);
    return Convert.ToBase64String(nonce);
}
```

### Whitelisting external scripts

`strict-dynamic` can be used to allow scripts hosted on a third-party domain to be loaded by scripts that you control. However, at the time of writing, this isn't supported by all major browsers, so a host whitelist should be used as well as a fallback until it has broad support.

```
script-src 'self' 'nonce-00deadbeef' 'unsafe-inline' 'strict-dynamic' https://example.com; object-src 'none'; base-uri 'none';
```

When using `strict-dynamic`, you will also need to add a `nonce` to any external scripts that are referenced.
```
<script nonce="00deadbeef" src="https://example.com/analytics.js" />
```

> **Note:**
> 
> Be careful what sources you whitelist. If any endpoints return JSONP and fail to sanitize the callback, it is possible to inject code. For example:
>
> ```html
> <script src="https://example.com/getuser?callback=window.location='http://google.com';test"></script>
> ```
>
> Might return `window.location='http://google.com';test({})` in the JSONP response, which would cause arbitrary code to be executed!

There are other policies that you can define to strengthen your site's security, such as restricting where stylesheets are loaded from. This post only focuses on mitigating cross-site scripting attacks.

### Further Reading

* [CSP 3](https://www.w3.org/TR/CSP3/)
* [CSP Evaluator](https://csp-evaluator.withgoogle.com/) can be used to spot vulnerabilities in a policy.
* [Introduction to CSP](https://csp.withgoogle.com/docs/index.html)
* [Mozilla Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)

Thanks to Bradley Grainger and Kyle Sletten for reviewing this implementation.