strict-dynamic
in CSPHere is an example Content-Security-Policy
that uses strict-dynamic
:
script-src 'nonce-rAnd0m' 'strict-dynamic';default-src 'self';
Now we can simply use a nonce to load our scripts:
<script src="/script-loader.js" nonce="rAnd0m"></script>
The key super power of strict-dynamic
is that it will allow /script-loader.js
to load additional scripts via non-"parser-inserted" script elements.
So how do you create a non-"parser-inserted" script element? Here's an example:
var s = document.createElement('script'); s.src = "https://cdn.example.com/some-script-you-need.min.js"; document.body.appendChild(s);
You can also use other mechanisms to create a script loader, such as webpack, RequireJS, head.js, yepnope.js, etc.
Since this is a new feature of CSP (CSP Level 3), if someone is using an older browser that may only have CSP Level 2 support, using the above policy the request to load a script from cdn.example.com
would be blocked. Support for nonce would still work in CSP Level 2 browsers, but not in CSP Level 1.
Thankfully the authors of CSP Level 3 considered this, and have a clever workaround. When strict-dynamic
is used, browsers that support it will ignore the following source list expressions:
'unsafe-inline'
'self'
http:
and https:
)In fact you might see something like this in your developer tools console:
content security policy: Ignoring “'unsafe-inline'” within script-src: ‘strict-dynamic’ specified
content security policy: Ignoring “http:” within script-src: ‘strict-dynamic’ specified
content security policy: Ignoring “https:” within script-src: ‘strict-dynamic’ specified
content security policy: Ignoring “'unsafe-eval'” within script-src: ‘strict-dynamic’ specified
content security policy: Ignoring “'report-sample'” within script-src: ‘strict-dynamic’ specified
So our script can be made backwards compatible by doing something like this:
script-src 'nonce-rAnd0m' 'strict-dynamic' https: 'self';default-src 'self';
Above we have added 'self' to allow loading of /script-loader.js
and we have added https:
to allow loading of https://cdn.example.com/some-script-you-need.min.js
You may notice that by adding https:
we are allowing any domain to load a script, you could be more specific if you wanted to. In our example the following would be better:
script-src 'nonce-rAnd0m' 'strict-dynamic' cdn.example.com 'self';default-src 'self';
If we had an inline script block then we could consider adding 'unsafe-inline'
to the policy to allow it to load on CSP Level 1 Browsers. You'll need to balance the complexity of your policy vs the breadth of browsers that you want to support.
On older browsers you may see something like this:
the source list for content security policy directive 'script-src' contains an invalid source: "strict-dynamic". it will be ignored.
This message means that the browser version does not support strict-dynamic
. It will be ignored, and the browser will just use the remainder of the script-src
Content Security Policy Directive without the strict-dynamic
in the source list.
You can use strict-dynamic
in a script-src or default-src directive. When it is in the default-src it only applies to loading of scripts.
If one of your trusted scripts loads JavaScript using a safe method (document.createElement), then it can load arbitrary scripts with strict-dynamic
. However if your trusted scripts attempt to load an image, a stylesheet, etc., then the img-src
, and style-src
directives still apply.
The strict-dynamic
directive was added to CSP Level 3. It is currently supported in Chrome 52+ / Edge 79+, Firefox 52+ and Safari 15.4.
Not supported at all in Internet Explorer
Want to learn the ins and outs CSP? Grab a copy of the CSP Developer Field Guide. It's a short and sweet guide to help developers get up to speed quickly.
Grab a CopyAdvisory Week is a weekly roundup of all the security advisories published by the major software vendors.