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

01

Which scripts does the page run?

learner@web-lab
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.

02

Read the second version

learner@web-lab
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.

03

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.

learner@web-lab
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.

04

Check the policy behind it

learner@web-lab
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

promo
<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.

promo-v2
<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

  1. 1Every external script is code someone else can change.
  2. 2Self-host where you can; pin with SRI where you cannot.
  3. 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.