The one-sentence version
Any piece of JavaScript in a webpage possesses the same authority as the page itself: it can read forms, modify content, and send requests on your behalf. CSP (Content Security Policy) tells the browser "only run scripts from these specific locations," blocking injected malicious scripts at the door; SRI (Subresource Integrity) tells the browser "the hash of this script must be this specific value," ensuring that if a file on a third-party server is tampered with, it will be rejected. For pages handling money and passwords, the most secure approach is: zero third-party scripts.
This article has three layers:
- Layer 1: The Intuition: Why a single external script can destroy a website
- Layer 2: Real-World Use: British Airways Magecart, polyfill.io, Ledger Connect Kit
- Layer 3: Principles and Engineering details
Layer 1: The Intuition
1.1 External Scripts = Handing Over Your House Keys
Many websites reference third-party code like this:
<script src="https://cdn.some-analytics.com/track.js"></script>
This line of code means: "Fetch a piece of code from that server and run it as me." If that server is compromised, or the domain changes owners, whatever code it returns will be executed on your page, including instructions like "send the user's entered credit card number to me."
1.2 Two Lines of Defense
| Defense | What it controls | Analogy |
|---|---|---|
| CSP | Which sources of code are allowed to run | A security guard only letting employees on a whitelist into the building |
| SRI | What the content of a specific file must be | A package with a security seal; if the seal is broken, it is rejected |
Layer 2: Real-World Use
| Event | What happened | Which defense could have stopped it |
|---|---|---|
| British Airways (2018) | Attackers modified a JavaScript library on the BA website, adding 22 lines of code to send payment page card numbers to a fraudulent domain. Approximately 380,000 transactions were affected, resulting in a £20 million fine from UK regulators. | CSP's connect-src can prevent data from being sent to unknown domains |
| polyfill.io (2024) | A public script service used by hundreds of thousands of websites had its domain sold, after which it began serving malicious redirect code to some visitors. | SRI would prevent the tampered file from executing; more thoroughly, one should avoid referencing external CDNs entirely |
| Ledger Connect Kit (2023) | Attackers used phishing to obtain a former employee's npm account and published a new version containing a "token stealer"; many DeFi frontends automatically loaded the latest version via CDN, resulting in user losses of approximately $600,000 within hours. | Lock versions + SRI, or bundle dependencies into your own code |
This category of attack is collectively known as Magecart (web skimming) or supply chain attacks: the attacker does not touch your server, but instead modifies the third-party code you trust.
Layer 3: Principles and Engineering
3.1 A Strict CSP
Content-Security-Policy:
default-src 'none';
script-src 'self';
style-src 'self';
img-src 'self' data:;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'none';
form-action 'self';
report-to csp-endpoint
| Directive | Meaning |
|---|---|
default-src 'none' |
Deny everything by default, then allow specific items below |
script-src 'self' |
Only run script files from this site; inline scripts and eval are strictly prohibited, which is key to blocking most XSS |
connect-src |
The page can only send fetch / WebSocket to these addresses; even if malicious scripts sneak in, they cannot steal data |
frame-ancestors 'none' |
Cannot be embedded in an iframe, preventing clickjacking |
base-uri 'none' |
Prevents injecting <base> tags that could tamper with all relative links |
form-action 'self' |
Forms can only be submitted to this site |
report-to |
The browser automatically reports violations; this can be used as an intrusion alert |
Before going live, you can use Content-Security-Policy-Report-Only to report without blocking, observe for a period, and only enable it officially once you have confirmed there are no false positives.
3.2 Writing SRI
<script src="https://cdn.example.net/lib-4.2.1.min.js"
integrity="sha384-(SHA-384 hash of file contents, base64 encoded)"
crossorigin="anonymous"></script>
After the browser downloads a file, it calculates the hash; if it doesn't match, execution is refused. It relies on the second pre-image resistance property of hash functions: an attacker cannot create a malicious file with different content but the same hash (see "The Birthday Paradox and Hash Collisions").
Limitations: SRI can only be used for fixed-version files. If you reference an "always latest version" URL, the content will change, the hash will change, and SRI will not work. This "always latest version" approach was precisely the root cause of the Ledger Connect Kit incident.
3.3 Zero Third-Party Scripts
For pages involving signatures, payments, or logins, the most secure approach is:
- All dependencies must lock versions and be bundled into your own code during build time, rather than being loaded from any CDN at runtime;
- Do not include any third-party scripts such as analytics, advertising, or customer chat;
- Dependency upgrades should follow a manual review process; do not enable automatic merging;
- Note that the hosting platform itself may inject things into the page (analytics, performance optimization, email obfuscation scripts); these need to be disabled, or you must use
Cache-Control: no-transformand perform regular inspections (see "The Bybit Incident and Frontend Integrity").
3.4 What CSP Cannot Prevent
- If an attacker can directly modify files on your server (the source of
'self'), CSP will still allow them to run; - If the build process is poisoned, the malicious code is already inside "your own code."
These two scenarios must be defended against through deployment pipeline security and online integrity inspections.
Summary
| Layer | Key points |
|---|---|
| Intuition | External scripts run as you, which is equivalent to handing over your house keys |
| CSP | Limits code sources and data destinations; bans inline scripts and eval; use violation reporting as alerts |
| SRI | Uses hashes to "seal" files; only applicable to fixed versions |
| Best Practice | Zero third-party scripts for sensitive pages; lock dependency versions, bundle them, and use manual reviews |