Unit 6: The browser security model · Lesson 5 of 5 · 20 min
Case file: the script host that changed hands
Find pages that run scripts from domains you do not control, then pin them with Subresource Integrity and a strict CSP.
case file
Polyfill.io domain takeover
polyfill.io and the websites that embedded it · 2024
In February 2024 the polyfill.io domain and its GitHub account were acquired by a new owner. In June 2024 researchers reported the service was injecting malicious code into more than 100,000 sites that loaded it, redirecting some mobile visitors to scam sites.
What went wrong
Sites loaded executable JavaScript from a third-party domain at runtime, so whoever owned the domain could run code in every visitor's browser.
What would have stopped it
- Self-host third-party scripts or pin them with Subresource Integrity hashes.
- Use a Content Security Policy that limits script sources, and review it when dependencies change.
- Watch ownership changes for domains your site loads code from, and remove ones you no longer need.
What defenders could have seen
- Client-side monitoring flagging changed content from a third-party script URL.
- CSP violation reports or user reports of unexpected redirects.
sourcessansec.ioblog.cloudflare.com
mission
- You are
- The analyst reviewing the shop's marketing pages.
- Situation
- Marketing added a promo page that loads a polyfill from an outside CDN. The developers also built a second version.
- Goal
- Find what the first page trusts and confirm the second one pins its script.
- Hand in
- The external host, the integrity hash and a recommendation.
Subresource Integrity as a tamper seal
Which scripts does the page run?
curl -s http://127.0.0.1:8000/promo | grep -o '<script[^>]*>' <script src="https://cdn.polyfill-mirror.example/v3/polyfill.min.js"> curl -sI http://127.0.0.1:8000/promo | grep -i content-security-policy Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.polyfill-mirror.example
you should see A script from cdn.polyfill-mirror.example with no integrity attribute, and a CSP that explicitly allows that host.
defender tip A CSP that allowlists a host protects you only as long as that host stays trustworthy. It does nothing if the host itself turns bad.
Read the second version
curl -s http://127.0.0.1:8000/promo-v2 | grep -o '<script[^>]*>'
<script src="/static/polyfill.min.js" integrity="sha384-CqV2cP8D5x5HuLdJmeYm6V4n4moKTzQ3HwXmMrmV4wXlM8LlztG/WmjsS8TYt35S" crossorigin="anonymous">
you should see The script is now served by the shop itself, with an integrity hash and crossorigin.
Compute the hash yourself
An SRI value is the base64 of the file's SHA-384 digest. If the file changes by one byte, the browser refuses to run it.
curl -s http://127.0.0.1:8000/static/polyfill.min.js | openssl dgst -sha384 -binary | openssl base64 -A; echo
CqV2cP8D5x5HuLdJmeYm6V4n4moKTzQ3HwXmMrmV4wXlM8LlztG/WmjsS8TYt35S
you should see The same value as in the page's integrity attribute.
Check the policy behind it
curl -sI http://127.0.0.1:8000/promo-v2 | grep -i content-security-policy
Content-Security-Policy: default-src 'self'; script-src 'self'; require-trusted-types-for 'script'
you should see script-src 'self': no outside host can serve scripts to this page at all.
The promo page's script
<script src="https://cdn.polyfill-mirror.example/v3/polyfill.min.js"></script> script-src 'self' https://cdn.polyfill-mirror.example
Runs whatever that domain sends today.
<script src="/static/polyfill.min.js" integrity="sha384-..." crossorigin="anonymous"></script> script-src 'self'
Runs only the exact bytes that were reviewed.
What a defender takes away
- 1Every external script is code someone else can change.
- 2Self-host where you can; pin with SRI where you cannot.
- 3Review the list of script hosts whenever a domain changes hands.
Questions
0 of 2 solved
Answer each one from what you find on your lab machine.