Strict-Dynamic Implementation
Strict-Dynamic Implementation
The 'strict-dynamic' keyword represents a paradigm shift in CSP, allowing scripts to load additional scripts dynamically while maintaining security:
// Strict-Dynamic CSP Implementation
class StrictDynamicCSP {
constructor() {
this.trustedScripts = new Set();
this.config = {
useFallback: true,
fallbackSources: ["'self'", 'https:']
};
}
generateStrictDynamicPolicy(nonce) {
// Modern browsers supporting strict-dynamic
const modernPolicy = {
'default-src': ["'none'"],
'script-src': [`'nonce-${nonce}'`, "'strict-dynamic'"],
'style-src': ["'self'", `'nonce-${nonce}'`],
'img-src': ["'self'", 'data:', 'https:'],
'font-src': ["'self'"],
'connect-src': ["'self'"],
'frame-ancestors': ["'none'"],
'base-uri': ["'none'"]
};
// Fallback for older browsers
if (this.config.useFallback) {
modernPolicy['script-src'].push(...this.config.fallbackSources, "'unsafe-inline'");
}
return this.formatPolicy(modernPolicy);
}
// Trusted Types integration for strict-dynamic
implementTrustedTypes() {
return `
// Create Trusted Types policies
if (window.trustedTypes && window.trustedTypes.createPolicy) {
const scriptPolicy = window.trustedTypes.createPolicy('default', {
createScript: (script) => {
// Validate script content
if (this.validateScriptContent(script)) {
return script;
}
throw new Error('Script validation failed');
},
createScriptURL: (url) => {
// Validate script URL
if (this.validateScriptURL(url)) {
return url;
}
throw new Error('Script URL validation failed');
}
});
// Override dynamic script creation
const originalCreateElement = document.createElement;
document.createElement = function(tagName) {
const element = originalCreateElement.call(document, tagName);
if (tagName.toLowerCase() === 'script') {
// Monitor script creation
console.log('Dynamic script created');
}
return element;
};
}
`;
}
// Loader for strict-dynamic environments
createSecureScriptLoader() {
return `
class SecureScriptLoader {
constructor(nonce) {
this.nonce = nonce;
this.loadedScripts = new Set();
}
async loadScript(src, options = {}) {
// Prevent duplicate loading
if (this.loadedScripts.has(src)) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
// Inherit nonce from parent (strict-dynamic behavior)
script.nonce = this.nonce;
// Set script attributes
script.src = src;
script.async = options.async !== false;
script.defer = options.defer || false;
// Set integrity if provided
if (options.integrity) {
script.integrity = options.integrity;
script.crossOrigin = 'anonymous';
}
script.onload = () => {
this.loadedScripts.add(src);
resolve();
};
script.onerror = () => {
reject(new Error(\`Failed to load script: \${src}\`));
};
// Append to document
(document.head || document.documentElement).appendChild(script);
});
}
async loadScripts(scripts) {
return Promise.all(
scripts.map(script =>
typeof script === 'string'
? this.loadScript(script)
: this.loadScript(script.src, script)
)
);
}
}
// Initialize global loader
window.scriptLoader = new SecureScriptLoader(document.currentScript.nonce);
`;
}
formatPolicy(policy) {
return Object.entries(policy)
.map(([directive, sources]) => `${directive} ${sources.join(' ')}`)
.join('; ');
}
}
// React application with strict-dynamic
function StrictDynamicReactApp() {
const [nonce] = useState(() => window.__CSP_NONCE__);
useEffect(() => {
// Dynamically load dependencies with strict-dynamic
window.scriptLoader.loadScripts([
{
src: 'https://cdn.example.com/analytics.js',
integrity: 'sha384-...',
async: true
},
{
src: '/js/feature-detection.js',
defer: true
}
]).then(() => {
console.log('Dynamic scripts loaded securely');
});
}, []);
return (
<div>
<script nonce={nonce} dangerouslySetInnerHTML={{
__html: `
// This inline script is allowed due to nonce
console.log('Strict-dynamic allows this script');
// And this script can load more scripts
import('/js/dynamic-module.js').then(module => {
console.log('Dynamic import successful');
});
`
}} />
</div>
);
}