SRI and CSP: Complementary Security Controls
Subresource Integrity (SRI) and Content Security Policy (CSP) are often discussed together because they address related security concerns but in different ways. Understanding how they complement each other is essential for building comprehensive web application security. While they solve different problems and operate at different layers, implementing both creates a defense-in-depth approach that significantly enhances protection against script injection attacks.
What SRI and CSP Do
SRI verifies content integrity - It ensures that a resource you load hasn't been modified. When you add an integrity attribute to a script tag, the browser verifies that the downloaded content matches the hash you specified. If it doesn't match, the script is rejected.
CSP controls what can be loaded - It specifies where resources can come from and what they're allowed to do. CSP prevents loading resources from unauthorized sources and can prevent execution of inline scripts entirely.
These are distinct security mechanisms addressing different attack vectors.
The Attack Scenarios They Protect Against
Consider these scenarios to understand the differences:
Scenario 1: CDN Compromise
An attacker gains access to a CDN hosting your JavaScript libraries. They modify the hosted library to include malicious code.
- SRI prevents this: The browser verifies the library's hash, detects the modification, and rejects the malicious script.
- CSP doesn't prevent this: CSP allows scripts from that CDN, so the malicious script would be accepted.
SRI is the primary defense here.
Scenario 2: Attacker Injects Script From Unapproved Origin
An attacker exploits a vulnerability to inject a script tag that loads JavaScript from their own malicious server.
- SRI can't prevent this: SRI only validates content integrity, not where it comes from. If the attacker controls the injected tag, they also control the integrity hash.
- CSP prevents this: CSP specifies allowed script sources. If the attacker's domain isn't on the allowlist, the browser rejects the script regardless of its integrity attribute.
CSP is the primary defense here.
Scenario 3: Inline Script Injection
An attacker injects inline JavaScript directly into your HTML.
- SRI can't prevent this: SRI applies to external resources with
srcattributes, not inline scripts. - CSP prevents this: CSP can restrict or prevent inline scripts entirely, requiring scripts to be external and from approved sources.
CSP is the primary defense here.
Scenario 4: Website Forwarded Through Proxy
Your website is served through an intercepting proxy (common in some corporate networks), and the proxy modifies your scripts while forwarding them.
- SRI prevents this: The modified content fails integrity verification, protecting against proxy-based attacks.
- CSP doesn't prevent this: CSP allows scripts from your own origin, so even modified content would be allowed.
SRI is the primary defense here.
How They Work Together: The Layers of Security
A comprehensive approach uses both SRI and CSP:
<!-- CSP prevents loading scripts from unauthorized sources -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://cdn.example.com">
<!-- SRI verifies that the allowed script hasn't been modified -->
<script src="https://cdn.example.com/library.js" integrity="sha256-abc123..."></script>
The CSP header ensures scripts only load from your domain or a specific trusted CDN. The SRI integrity attribute verifies that the script from that trusted source hasn't been modified. Together, they address different attack vectors:
- CSP: "Only load scripts from sources we trust"
- SRI: "Even if we load from a trusted source, verify it hasn't been modified"
This defense-in-depth approach means an attacker would need to compromise multiple layers to successfully inject malicious code.
CSP Restrictions on Script Attributes
CSP can actually affect how you use SRI. Modern CSP implementations interact with script attributes:
With script-src 'self', you can load scripts from your own origin:
<script src="/my-script.js" integrity="sha256-..."></script>
With script-src 'self' https://trusted-cdn.com, you can load from your origin and a trusted CDN:
<script src="https://trusted-cdn.com/lib.js" integrity="sha256-..."></script>
CSP's allowlist acts as a gatekeeper—only sources on the list are even attempted. SRI then verifies the content from those approved sources.
Integrity Hashes and CSP Nonces: Different Approaches to Inline Scripts
CSP and SRI take different approaches to securing inline scripts:
CSP with nonces - Allows specific inline scripts identified by a nonce:
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abc123'">
<script nonce="abc123">
console.log('This inline script is allowed');
</script>
CSP with hashes - Allows inline scripts matching a specific hash:
<meta http-equiv="Content-Security-Policy" content="script-src 'sha256-abc123'">
<script>
const staticCode = 'never changes';
</script>
SRI applies to external resources and doesn't directly protect inline scripts. This is a key distinction: CSP has mechanisms for inline scripts (nonces and hashes), while SRI focuses on external resources.
Practical Implementation: Combining Both
Step 1: Implement CSP
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://fonts.googleapis.com">
This CSP allows scripts from your own origin and a specific CDN, and styles from your origin and Google Fonts.
Step 2: Add SRI to External Resources
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto" integrity="sha256-...">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js" integrity="sha256-..."></script>
Each external resource has an integrity attribute verifying its content.
Step 3: Secure Inline Scripts With Nonces
<script nonce="random-value-123">
const apiUrl = 'https://api.example.com';
</script>
The nonce prevents injection of unauthorized inline scripts.
Reporting and Monitoring
Both SRI and CSP can report violations:
CSP Reporting - You can configure CSP to report violations to a logging endpoint:
Content-Security-Policy: script-src 'self' https://cdn.example.com; report-uri /csp-violations
Violations might indicate:
- Attempted injection of scripts from unauthorized sources
- Inline script injection attempts
- Misconfigured CSP that's too restrictive
SRI Violations - When SRI verification fails, the browser doesn't load the resource and often logs an error (though not all browsers report to CSP report-uri). You can detect this through:
- Browser console errors
- Service worker logs
- Application monitoring
Together, these reporting mechanisms provide visibility into both potential attacks and configuration problems.
Performance Implications
CSP Performance Impact - CSP has minimal performance impact. The browser just checks URLs against the allowlist.
SRI Performance Impact - SRI requires computing a hash of the downloaded content, which has a tiny performance cost. Modern browsers do this efficiently, but it's not completely free.
Combined Impact - Using both SRI and CSP together has minimal performance impact. The security benefits far outweigh any minor performance cost.
Common Mistakes in Using Both
CSP Too Permissive, Relying Only on SRI -
<!-- CSP allows scripts from anywhere -->
<meta http-equiv="Content-Security-Policy" content="script-src *">
<script src="https://example.com/script.js" integrity="sha256-..."></script>
This defeats the purpose of CSP. CSP should restrict the sources; SRI verifies the content. Using both relaxes into using neither.
SRI on Dynamic Content
<script src="/dynamic-config.js" integrity="sha256-..."></script>
If the script changes on each request (based on user, timestamp, etc.), the integrity hash becomes invalid. SRI should only be used on truly static resources.
Not Updating SRI When Updating Libraries -
<!-- Old library -->
<script src="/lib-1.0.js" integrity="sha256-old123"></script>
If you update the library without updating the integrity hash, the script fails to load.
Forgetting CSP Applies to Images and Fonts Too -
Content-Security-Policy: script-src 'self'
This CSP only restricts scripts. Images can come from anywhere. A more comprehensive CSP restricts all resources:
Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com; img-src 'self' data: *.example.com
Modern Framework Support
Modern frameworks often support SRI and CSP automatically:
Create React App - Generates SRI hashes for bundled assets automatically.
Next.js - Has built-in CSP support and can generate integrity attributes.
Django - Provides CSP middleware (django-csp) and can include SRI in templates.
Express.js - Helmet middleware provides CSP; SRI requires manual attribute addition.
Using framework built-in support reduces manual configuration errors.
Real-World Example: Comprehensive Security
<!DOCTYPE html>
<html>
<head>
<!-- CSP: Strict policy for scripts, styles, fonts -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' https://cdn.jsdelivr.net 'nonce-abc123';
style-src 'self' https://fonts.googleapis.com;
font-src https://fonts.gstatic.com;
report-uri /csp-violations">
<!-- External library with SRI: Verified from trusted CDN -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"
integrity="sha256-WZKhc00l73eJIGJ+FXbb4YvI8rKeXe7y3+3p5sznTw="></script>
<!-- Local script: From same origin, no integrity needed -->
<script src="/app.js"></script>
<!-- Inline script: Secured with nonce -->
<script nonce="abc123">
window.API_KEY = 'development-key';
</script>
</head>
<body>
<!-- Content -->
</body>
</html>
This example shows:
- CSP allowing scripts from same origin and a specific CDN, with a nonce for inline scripts
- SRI on the external library to verify it hasn't been modified
- Local scripts without SRI (they're from same origin, already protected by CSP)
- Inline script protected by nonce
Conclusion: Use Both for Maximum Security
SRI and CSP are complementary security mechanisms that address different attack vectors. CSP restricts where resources can come from; SRI verifies that approved resources haven't been modified. Together, they create a defense-in-depth approach that significantly improves security against script injection attacks. Modern web applications should implement both as part of their security strategy, using framework tools and build processes to manage SRI hashes and CSP policies automatically rather than manually maintaining them.

