Adopting strict CSP

To make an application compatible with strict CSP it is usually necessary to make some changes to HTML templates and client-side code, add the policy header, and test that everything works properly once the policy is deployed. This page walks you through the common steps.

Code changes

TIP: If you would first like to understand which parts of your application need to be changed to support CSP, review the adoption workflow section below. It guides you through the process of using the CSP Mitigator extension to identify patterns which might need to be refactored, and explains how you can fix them.

Add nonces to <script> elements

With strict CSP, every <script> element must have a nonce attribute containing a random, unguessable token which matches the value specified in the policy. The first step is to add these attributes to all scripts:

<script src="/path/to/script.js"></script>
<script>foo()</script>

becomes (depending on the template system syntax):

<script nonce="${nonce}" src="/path/to/script.js"></script>
<script nonce="${nonce}">foo()</script>

If you use Closure Templates , the template system will add nonce attributes automatically without requiring any changes to your templates if you pass the nonce value as injected data.

In other template systems you will need to modify the templates which include <script> tags to add the nonce attribute and set its value.

Some example code to generate a nonce is included in the FAQ.

Refactor inline event handlers and javascript: URIs

Inline event handlers (onclick="...", onerror="...") and <a href="javascript:..."> links can be used to run scripts, so an attacker who finds an XSS bug could inject such HTML and execute malicious JavaScript. CSP requires refactoring those patterns into safer alternatives.

In most cases the changes will be straightforward. To refactor event handlers, rewrite them to be added from a JavaScript block:

<script> function doThings() { ... } </script>
<span onclick="doThings();">A thing.</span>

should become:

<span id="things">A thing.</span>

<script nonce="${nonce}">
document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('things')
          .addEventListener('click', function doThings() { ... });
});
</script>

For javascript: URIs, you can use a similar pattern:

<a href="javascript:linkClicked()">foo</a>

into

<a id="foo">foo</a>

<script nonce="${nonce}">
document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('foo')
          .addEventListener('click', linkClicked);
});
</script>

Refactor calls to JS APIs incompatible with CSP

In addition to markup changes, some more rare JavaScript patterns may also have to be refactored to make the application CSP-friendly.

  • document.write() used to load additional scripts:

    document.write("<script src='/foo.js'></script>");
    

    should be changed to use document.createElement('script'), if possible. Alternatively, a nonce should be included in the string passed as an argument to document.write() (not recommended):

    var script = document.createElement('script');
    script.src = "/foo.js";
    document.head.appendChild(script);

    // or
    var nonce = goog.getScriptNonce(); document.write("<script nonce='" + nonce + "' src='/foo.js'></script>");

  • createElement('script') used to add inline scripts.

    In rare cases, new inline scripts are added to the page by creating a new script node and assigning to its .text property. If your application does this, you need to make sure to manually pass set the nonce attribute of the new script:

    var script = document.createElement('script');
    script.text = 'alert(123)';
    document.body.appendChild(script);
    

    should be:

    var script = document.createElement('script');
    script.nonce = goog.getScriptNonce();
    script.text = 'alert(123)';
    document.body.appendChild(script);
    
  • Uses of eval().

    If your application uses eval() to convert JSON string serializations into JS objects, you should refactor such instances to JSON.parse().

    If you cannot remove all uses of eval() you can still set a CSP policy, but you will have to use the 'unsafe-eval' CSP keyword which will make your policy slightly less secure.

Serve the Content-Security-Policy header

Finally, you need to enable CSP in your application. Many server-side frameworks provide convenience wrappers or configuration which allows you to set an application-wide policy; see for example the Django-CSP-Nonce module.

If your framework doesn't provide such functionality, you need to directly set the response header and manually specify the policy described on the strict CSP page. Due to implementation issues across browsers, it's best to send a CSP header only for Chrome >= 55, Firefox >= 43, and Opera >= 42.

Report-Only mode

Instead of immediately deploying a policy which enforces all CSP restrictions, you can use the report-only mode which will not affect the behavior of your application, but will still generate violation reports when patterns incompatible with CSP are encountered, and send them to a reporting endpoint defined in your policy. To use CSP in this mode, you should serve the policy in the Content-Security-Policy-Report-Only header.

Testing and deployment

Adoption workflow

The CSP Mitigator Chrome extension is a tool for identifying the parts of an application which have to be changed to support CSP. As you interact with your app it generates a report with all patterns described in the code changes section above.

We recommend adopting CSP using the following workflow:

  1. Install the CSP Mitigator extension.
  2. Click the "M" symbol in the Chrome toolbar and set the path to the root of your application (e.g. https://www.example.org/application/). The default policy of the CSP Mitigator is a good, secure starting point.
  3. Click the "Start" button.
  4. Interact with your application to exercise as much functionality as possible.
  5. When you’re done, click the "M" icon again and click "Stop" - you will see a report listing any patterns incompatible with CSP.
  6. Refactor your application as outlined in the report.
  7. After you make changes, repeat testing in the CSP Mitigator to see what issues remain. Once you've addressed all the reported issues, your application will support CSP!

Deploying to production

If local testing doesn't show any CSP violations you are likely ready to apply the policy in production -- either in Report-Only mode, or by gradually rolling out an enforcing policy.

A few final things to keep in mind:

  • If your product works with a testing team, it might be worth letting them know about the CSP deployment and ask them to keep an eye out for any CSP warning messages in the browser console.

  • To see violations reported by users' browsers, review the reports sent to the collection URL configured in the report-uri directive.