Frequently Asked Questions
- How do I generate nonces?
- How do I pass nonces to my templates?
- What if my site is static and I can't add nonces to scripts?
- What if my application uses Service Workers?
- How do I make my favorite JS library/widget/CMS compatible with nonce-based CSP?
- Why does the strict policy only set CSP directives that limit script execution?
- Why can't I keep using script whitelists in CSP?
- I want to use 'strict-dynamic', but also enforce a URL whitelist; is this possible?
- What kinds of XSS bugs *are not* mitigated by strict CSP?
- Does having CSP mean I can stop caring about XSS?
Adoption
How do I generate nonces?
Nonces should be cryptographically strong random values, at least 128 bits in length. An example of a Python function to create a nonce is:
def GetCspNonce(): """Returns a random nonce.""" NONCE_LENGTH = 16 return base64.b64encode(os.urandom(NONCE_LENGTH))
Note: It is important that nonces are unpredictable, and that a new nonce is generated for every page load. Otherwise, an attacker who can guess the correct value will be able to inject arbitrary scripts and bypass CSP.
How do I pass nonces to my templates?
This depends on the framework, but it works like passing any other string variable as a parameter to the template system. For example, using Django/Jinja, you would set:
nonce = GetCspNonce() template_values = { 'script_nonce' : nonce } jinja2.render_template('index.html', template_values)
Remember to make sure that the parameter name matches the value in the template!
In
Closure Templates,
nonces are inserted
automatically
by the template system based on the ij.csp_nonce
value.
What if my site is static and I can't add nonces to scripts?
In some cases, applications need to serve static HTML files without passing them through a template system. There are several approaches that work in this case:
Method #1: Use CSP hashes instead of nonces.
It's possible to calculate SHA hashes of static inline <script>
blocks with
this tool, or by omitting 'unsafe-inline'
and inspecting CSP violation warnings in the developer console of the browser,
containing hashes of blocked scripts, and add them to the policy as
'sha256-...'
. If the page loads external scripts, they can first be converted
to inline <script>
blocks.
For example, the following script:
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
can be rewritten as:
<body> <script> var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js']; scripts.forEach(function(scriptUrl) { var s = document.createElement('script'); s.async = false; s.src = scriptUrl; document.head.appendChild(s); }); </script> ... </body>
WARNING: The snippet above assumes that document.head
exists. In case it does
not, you must provide an alternative node to append scripts to.
Then, a hash of the loader script can be calculated and the page will work with a policy of:
Content-Security-Policy: script-src 'sha256-...' 'strict-dynamic' 'unsafe-inline' https:
Method #2: Pass the static file through a template system.
In some applications a simpler solution is to make the resources non-static: add nonce attributes which will be filled in by the template system, and render them like other application templates.
What if my application uses Service Workers?
Since Service Workers are bound by the policy set on the response which returns the worker script -- rather than the CSP of the page which loads it -- the simplest solution is to avoid sending a policy in Service Worker responses.
How do I make my favorite JS library/widget/CMS compatible with nonce-based CSP?
Many applications have dependencies on external components which might be incompatible with CSP; by refactoring them you can make it possible for your applications and others to adopt a strict CSP policy. Here are examples of useful refactoring work:
-
Rewriting patterns that are inherently incompatible with CSP (inline event handlers,
javascript:
URIs) -- both in any web UIs and in the markup generated by JS libraries. -
Adopting CSP and serving
Content-Security-Policy
headers (with a useful policy) in server-side components (for example in admin UIs of web frameworks). -
Adding script nonces, in markup generated server-side or when loading trusted subresources in JS libraries.
-
Implementing changes to facilitate CSP adoption, e.g. adding an "auto-noncing mode" to a template system (example) or extending a framework to make it easier to set CSP headers.
In some cases, such changes might qualify for a reward under the Google Patch Rewards program.
Security
Why does the strict policy only set CSP directives that limit script execution?
In the past, most applications which used CSP have set a number of directives
to restrict the loading of various types of resources, e.g. img-src
,
font-src
or style-src
. However, these directives do not constrain an
attacker who can inject a malicious script into the application; the ability to
inject scripts bypasses any other restrictions offered by CSP.
Since setting directives other than script-src
and object-src
doesn't
increase the protection against cross-site scripting, and it adds adoption and
maintenance costs of CSP, we believe most applications should focus on deploying
the baseline strict policy as the most high-impact improvement. Afterwards,
it is of course possible to add further directives, depending on the needs of
the application.
Why can't I keep using script whitelists in CSP?
The traditional approach of whitelisting domains from which scripts can be loaded is based on the assumption that all responses coming from a trusted domain are safe, and can be executed as scripts. However, this assumption does not hold for modern applications; some common, benign patterns such exposing JSONP interfaces and hosting copies of the AngularJS library allow attackers to escape the confines of CSP.
In practice, while it may not be obvious to application authors, the majority
of script-src
whitelists can be
circumvented by an attacker
with an XSS bug, and provide little protection against script injection. In
contrast, the nonce-based approach does not suffer from these
problems and makes it easier to adopt and maintain a more secure policy.
I want to use 'strict-dynamic', but also enforce a URL whitelist; is this possible?
On its own, 'strict-dynamic'
causes the browser to rely only on nonces or
hashes and ignore the URI whitelist in script-src
, for backwards
compatibility reasons. However, it's possible to enforce both a whitelist and
nonces with 'strict-dynamic' by setting two policies:
Content-Security-Policy: script-src foo.example.org bar.example.org 'unsafe-inline' Content-Security-Policy: script-src 'nonce-random123' 'strict-dynamic' 'unsafe-inline' https:
The browser will check each script against each policy separately and only
allow those which match both policies. In this case, it will accept scripts with
a nonce of random123
(second policy) which are either inline <script>
blocks or come from {foo,bar}.example.org
(first policy).
What kinds of XSS bugs are not mitigated by strict CSP?
Based on data from Google applications, around 25% of XSS bugs are exploitable even in applications which set a strict CSP policy. This includes:
-
Injections into the body of
<script>
elements. If the application doesn't properly escape user input inside a script which contains a valid nonce, malicious data can escape out of strings and inject its own code into the existing script. -
Injections into the URL of an external
<script>
. If attackers control thesrc
parameter of a script with a nonce, they can point the script to a URL they control (sometimes by using an open redirect). -
Injections into the locations of dynamically created scripts (
document.createElement('script')
), including into any library functions which createscript
DOM nodes based on the value of their arguments. This includes some common APIs such as jQuery's.html()
, as well as.get()
and.post()
in jQuery < 3.0. -
Template injections in Angular applications. An attacker who can inject an Angular template can use it to execute arbitrary JavaScript.
-
If the policy contains
'unsafe-eval'
, injections intoeval()
,setTimeout()
and a few other rarely used APIs.
Developers and security engineers should pay particular attention to such patterns during code reviews and security audits.
Does having CSP mean I can stop caring about XSS?
In short, no.
CSP is a defense-in-depth technique that can prevent the execution of unwanted scripts, but it's not a substitute for avoiding (and promptly fixing) XSS bugs. In particular, even with a good policy, the following caveats apply:
-
Not all browsers support CSP and some users will get no protection even if the application sets a safe policy.
-
Even if attackers cannot inject malicious scripts, they may be able to execute other kinds of attacks, such as scriptless or post-XSS attempts.
-
A small fraction of XSS vulnerabilities are not mitigated by CSP and if a flaw in your application was caused by such a bug, an attacker could still exploit it.
All that said, it is important to stress that CSP is a useful safety net which can protect applications from the exploitation of most kinds of XSS, for the majority of users.
What about browser support?
The 'strict-dynamic' keyword is available Chrome, Opera and Firefox, and is under consideration in Edge. Strict CSP policies are backwards-compatible with other browsers, but they do not protect against most XSS bugs. The main benefit they have in older browsers is protecting against bugs due to insecure URI schemes, i.e. they do not allow exploiting XSS via javascript: URIs.