Introduction
Implementing a Content Security Policy (CSP) is a highly effective way to mitigate Cross-Site Scripting (XSS) risks. However, configuring and maintaining the policy can become a major headache.
For websites utilizing third-party SDKs (such as Google Tag Manager, analytics beacons, payment widgets, or social sharing buttons), these scripts often dynamically load (inject) additional scripts from nested domains. This forces developers to maintain a long, fragile whitelist of external domains in the script-src directive.
The 'strict-dynamic' directive, introduced in CSP Level 3, solves this problem. This article explains how it works and how to apply it.
1. The Limitations of Domain Whitelisting in CSP
In older versions of CSP (Levels 1 and 2), developers relied on whitelisting specific domains allowed to load and execute scripts:
Content-Security-Policy: script-src 'self' https://www.google-analytics.com https://cdn.jsdelivr.net;
Challenges with Whitelisting
- Maintenance Overhead:
If a third-party SDK (e.g.,
google-analytics.com) updates its internal logic to fetch a helper script from a new domain (e.g.,static.cloudflareinsights.com), your website will block the new script. You must update your CSP headers immediately to restore functionality. - Whitelist Bypasses:
If a whitelisted CDN (e.g.,
cdnjs.cloudflare.com) hosts an outdated library with known vulnerabilities or a script that allows arbitrary execution, an attacker can use it to bypass your CSP and execute malicious code.
2. How ‘strict-dynamic’ Works
The 'strict-dynamic' directive simplifies script trust management by establishing a trust chain.
Core Rules
- Trust Chain Propagation:
When
'strict-dynamic'is enabled, any script that is already trusted (via a cryptographicnonceorhash) is allowed to dynamically load new script tags. The browser automatically trusts the children of trusted scripts. - Domain Whitelist Override:
When a browser detects
'strict-dynamic'in your CSP, it ignores any domain-based source expressions (likehttps://example.com) and'self'within thescript-srcdirective. Trust is determined strictly by the cryptographic nonces or hashes.
3. Implementation Example
To use 'strict-dynamic', you must pair it with either a cryptographic nonce (a random token generated per request) or a file hash.
HTTP Header Configuration
Content-Security-Policy: script-src 'nonce-RndOmValue123' 'strict-dynamic' 'unsafe-inline' https:;
'nonce-RndOmValue123': Trusts any inline or external script tag containing this matching nonce attribute.'strict-dynamic': Instructs the browser to trust any child script dynamically injected by a nonce-trusted script.'unsafe-inline' https:: Fallback options for older browsers that do not support CSP Level 3. Modern browsers automatically ignore these when'strict-dynamic'is present.
HTML Markup Example
<!-- The browser executes this script because of the matching nonce attribute -->
<script nonce="RndOmValue123">
// Since this parent script is trusted, strict-dynamic allows it to inject child scripts
const script = document.createElement('script');
script.src = 'https://example.com/dynamic-library.js';
document.head.appendChild(script);
</script>
With this setup, you do not need to add https://example.com to your CSP headers. The browser executes dynamic-library.js because it was injected by a trusted parent script.
4. Key Considerations: Parser-Injected Scripts
Be aware that some legacy libraries inject scripts using document.write('<script src="..."></script>'). These parser-injected scripts are not trusted by 'strict-dynamic' and will be blocked by the browser.
To resolve this, you must update the script loading code to use standard, modern DOM APIs like document.createElement('script').
Conclusion
Adopting the 'strict-dynamic' directive in your CSP increases security while simplifying maintenance.
- Eliminate the need to maintain a long whitelist of third-party domains.
- Allow trusted parent scripts to dynamically inject dependencies.
- Include fallback expressions (
'unsafe-inline' https:) to support older browsers.
Consider using 'strict-dynamic' and cryptographic nonces in your project’s CSP configuration to secure your application.
