Implementing Dynamic Script Loaders with Integrity
Modern frontend architectures increasingly rely on programmatic script injection to optimize initial bundle sizes and defer non-critical execution. However, dynamic DOM manipulation expands the attack surface for supply chain compromises. Implementing Subresource Integrity (SRI) & Supply Chain Hardening requires a deterministic pipeline where browser DOM injection points are validated against precomputed SHA-384 hashes before execution. The origin server acts as the authoritative hash distributor, while edge/CDN layers serve immutable payloads. This guide details production-ready patterns for Asset Hashing & Dynamic Script Injection to guarantee cryptographic verification at runtime.
Architecting the Integrity-First Loader
The execution pipeline must guarantee that no external script evaluates without prior cryptographic validation. We begin by defining a strict initialization sequence that intercepts standard fetch events and maps them to controlled DOM insertion points.
Loader initialization sequence Instantiate a centralized loader module that queues script requests. The loader maintains an internal registry mapping asset URLs to their corresponding SHA-384 digests. This registry must be populated synchronously before any network requests fire.
Hash attachment to script elements
Before appending to the DOM, the loader attaches the integrity attribute. This attribute must exactly match the build-time digest. Any deviation triggers a browser-level execution block.
Execution timing controls
Leverage async or defer attributes based on dependency graphs. The loader defers downstream execution until the onload event fires. This ensures the browser’s native SRI verifier completes validation before dependent modules initialize.
Build Pipeline Integration
Build-time determinism is non-negotiable. Hash drift during post-processing breaks runtime verification and invalidates the entire trust chain. Integrate Dynamic Script Loading Patterns to align async/defer attribute mapping with manifest outputs.
Webpack/Vite plugin configuration Use dedicated SRI plugins to compute digests after minification but before manifest emission. Hashes must be calculated on the final artifact, not the source.
// webpack.config.js
const SubresourceIntegrityPlugin = require('webpack-subresource-integrity');
module.exports = {
plugins: [
new SubresourceIntegrityPlugin({
algorithms: ['sha384'],
enabled: process.env.NODE_ENV === 'production'
})
]
};
Manifest parsing logic
The build artifact must expose a JSON map of filename -> integrity_hash. A lightweight parser reads this at runtime to populate the loader registry. Parse failures should abort the initialization sequence immediately.
Runtime hash injection Inject the manifest as an inline JSON blob or fetch it via a dedicated endpoint. Ensure the manifest itself is served with immutable cache headers. This prevents tampering during transit.
Runtime Validation & Execution
The core injection logic must enforce strict MIME type checking and handle cross-origin resource sharing correctly. Browsers enforce SRI only when specific network conditions are met.
createElement workflow Programmatically generate script nodes with explicit integrity constraints. The loader factory must standardize attribute assignment across all injection points.
function loadScriptWithIntegrity(url, hash) {
const script = document.createElement('script');
script.src = url;
script.integrity = `sha384-${hash}`;
script.crossOrigin = 'anonymous';
script.type = 'module';
document.head.appendChild(script);
return new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = (e) => reject(e);
});
}
Cross-origin attribute handling
SRI verification fails silently on opaque responses. Always set crossOrigin="anonymous" to force CORS preflight. This ensures the browser receives a transparent response for hash comparison.
Error boundary wrapping
Wrap loader invocations in try/catch blocks. On onerror, capture the failure reason. If the integrity check fails, the browser blocks execution and fires an error event. Log this to telemetry without halting the core application flow.
Security Policy Alignment
Dynamic injection must operate within a strict Content Security Policy. Misaligned directives will either block legitimate scripts or create bypass vectors for attackers.
CSP header configuration
Deploy a script-src directive that explicitly permits self and specific trusted origins. Use report-only mode during initial rollout to audit violations before enforcing blocks.
# nginx.conf
add_header Content-Security-Policy-Report-Only "script-src 'self' 'strict-dynamic' 'sha384-<base64-hash>' https://cdn.example.com; report-uri /csp-violation;" always;
Hash vs nonce precedence Nonces are ephemeral and ideal for inline scripts. For external dynamic scripts, SRI hashes provide stronger supply chain guarantees. Do not mix both unless explicitly required by legacy constraints.
Compliance reporting endpoints
Route report-uri payloads to a centralized SIEM. Parse violated-directive and blocked-uri fields to detect hash mismatches or unauthorized injection attempts.
Fallback Strategy & Rollback Path
Initial deployments must operate in Content-Security-Policy-Report-Only mode. This allows security teams to observe real-world hash validation without breaking production.
If an integrity mismatch occurs, the loader triggers a retry against a versioned fallback endpoint (e.g., /v1/fallback/asset.js). Implement a circuit breaker after three consecutive failures. Telemetry alerts should fire on mismatch events. Core application flow must degrade gracefully rather than block entirely.
Common Pitfalls & Mitigation
- Hash drift: Caused by post-build minification or environment-specific transformations. Compute hashes as the final build step.
- Missing
crossOrigin='anonymous': Leads to opaque responses and silent SRI failure. Enforce attribute assignment in the loader factory. - CSP strict-dynamic conflicts:
strict-dynamicignores nonces/hashes for scripts not explicitly whitelisted. Use explicit hash whitelisting or disable strict-dynamic for dynamic loaders. - CDN cache poisoning: Mismatched hashes across edge nodes. Implement cache-busting query parameters and validate edge cache consistency via health checks.
- Fallback script over-reliance: Fallbacks without integrity validation reintroduce the original vulnerability. Apply identical SRI constraints to all fallback endpoints.
DevOps & Compliance Verification
Validate implementation using Chrome DevTools. Open the Network tab and locate the injected script. Inspect the Console for Failed to find a valid digest in the 'integrity' attribute warnings during negative testing.
Use curl -I -H "Accept: text/javascript" <url> to verify Content-Type and CORS headers match loader expectations. Confirm that Access-Control-Allow-Origin returns * or the exact requesting origin. Compliance teams should audit the SIEM for securitypolicyviolation events weekly.