Skip to content
leeveel
← Index
Nº 016 · · 6 min read#security

Every script on your payment page can read the card

Every script on a payment page has the same access as your own code, including to what the customer types. British Airways lost £20m to twenty-two lines of JavaScript. PCI DSS now asks you to know every script on the page and to notice when one changes. Here's what that means — and how to start in an afternoon.

by Nephele K.

++++

A payment page is a room where the customer reads their card number out loud. Every script on the page is in the room. Your checkout code is in there. So is the payment provider's SDK, the tag manager, the chat widget support asked for, the heat-map marketing asked for, and whatever any of those loads next. The browser gives each of them the same access — to the form, the keystrokes and the network. We built Roll Call, a free checker that lists who's on a payment page, because "what runs on your checkout?" is the question we ask first. It's also the one fewest teams can answer from memory. Here's the reasoning behind it, and the order we'd work in.

Why does a script on the checkout matter?

Because a script tag isn't a guest. Anything loaded into the page runs with the page's own permissions. It can read any field, listen to every keystroke and send what it hears anywhere a request can go. First-party or third-party is a question of who can change the file, not of what the file can do. That's the whole mechanism behind e-skimming, the family of attacks usually called Magecart. In 2018 British Airways served a library it already used, modified by twenty-two lines of JavaScript that copied card details when the form was submitted. The ICO's fine came to £20m — just under a million pounds a line. The same year Ticketmaster's payment page was skimmed through a chat widget. In 2024 polyfill.io, a domain embedded by more than a hundred thousand sites, changed owners in February and was serving malicious code by June. Nobody broke a card form. They were on the page, so they were in the room.

What do PCI DSS 6.4.3 and 11.6.1 ask for?

The page above is the problem in one picture. PCI DSS v4 answers it with two requirements that became mandatory on 31 March 2025. Requirement 6.4.3 asks for an inventory of every script on a payment page — each one authorised, each with a written business justification, each with a method that assures its integrity. Requirement 11.6.1 asks for a mechanism that detects unauthorised changes to those scripts and to the page's security-impacting HTTP headers, as the customer's browser receives them. At least weekly, unless a documented risk analysis says otherwise. Together, as we read them: know what runs, be able to say why, notice when it changes. Neither requirement names a technology. The Council's guidance offers Content Security Policy and Subresource Integrity as examples, not as the answer.

Our card form is in the provider's iframe. Does this apply?

Yes — to the page around it, and that's the part we get asked about most. The scripts inside your payment provider's iframe are the provider's responsibility. The scripts on the page that embeds it are yours, and that page is the one the customer is looking at. A script on your page can't read inside the iframe. It can replace it, cover it with a form of its own, or change where the button posts. That's why SAQ A, the shortest self-assessment, changed in January 2025. Requirements 6.4.3 and 11.6.1 are no longer line items; instead the merchant confirms, as a condition of eligibility, that the site isn't susceptible to attacks from scripts. The Council's FAQ 1588 names the techniques in those two requirements as one way to show it. The paperwork got shorter. The question didn't go anywhere.

What goes in the inventory?

The row above is what 6.4.3 wants for each script, split by who can fill it in. The left half is observable: the script's URL — or its hash, for an inline block — the host and whether it's yours, and what loaded it. Then a hash of the file as it is right now, whether the tag carries an integrity attribute, and whether the policy allows it. Our checker fills that half from the HTML as a browser receives it, before anything runs. It fetches up to forty script files and hashes each one. It flags tag managers from Google, Tealium, Adobe and Segment, because they add scripts it can't see, and it lists the iframes. The right half is yours, and no tool can fill it: why the script is here, who owns it, who authorised it, how you'd know it changed. The CSV export has two of those columns, justification and authorised by, left empty for you. The justification column is where we've watched inventories get honest. The chat widget and the heat-map rarely survive it, and the shortest list is the strongest control.

How do you prove a script hasn't changed?

Three ways, and they suit different scripts. Subresource Integrity puts the file's hash in the tag, and the browser refuses to run a file that doesn't match. It's right for a versioned file that never changes. It's wrong for one the provider updates in place, which describes most payment SDKs we've met. It also fails silently: nobody gets an alert, the checkout just stops working, which is a kind of alert. A Content Security Policy says which hosts may serve scripts to the page and where the page may send data. A policy with 'unsafe-inline' or a wildcard in it says almost nothing, so we start report-only, read the reports, then enforce. Change detection is the third: hash the page and every script the way the browser gets them, on a schedule, and alert on the difference. That one is what 11.6.1 actually asks for. Our checker tests SRI against the file it fetched and shows the integrity value for files you could pin. It reads the policy and lists which scripts it would block. Comparing two of its exports shows what appeared, disappeared or changed — a manual check, not the weekly mechanism.

What can't a checker like this see?

A fair amount, and we'd rather say so than have an assessor say it for us. We read the page as an anonymous visitor. A checkout behind a login or an empty basket needs a URL that loads without one, or a staging copy. We read the HTML before anything runs, so scripts a tag manager injects later aren't in the list — they're on the page all the same. We don't open the provider's iframe. We see the cookies the server sets, not the ones scripts set. And we can't tell you whether you're compliant, because no tool can. That is validated on the Council's forms, through your acquirer or a Qualified Security Assessor. There's no such thing as a PCI-certified script checker, ours included. We wrote the same warning about scan reports: the tool finds what it was told to find, and the work is the part it can't.

So where do you start?

In this order. One: run the roll call on the checkout URL and export the CSV. That's the afternoon. Two: fill the right half of every row, and strike anything that can't justify being on a payment page. The answer to marketing is usually a different page. Three: pin what's versioned with SRI and police everything with a CSP, report-only first. Four: keep tag managers off the payment page, as we do, or put everything they load into the inventory and under change control. Five: decide who checks it weekly and how they get told. A diff between two exports is fine for a Tuesday; a mechanism is what you need for a year. Six: take the list to whoever signs your assessment before they ask for it. If step five is the one you don't have a person for, that's the part we do.

What changed since we started saying this out loud: the payment page stopped being the page nobody touches. It never was. It was the page everybody touched — support with a widget, marketing with a pixel, a developer with a polyfill in 2019 — and nobody kept the list. The inventory turns a vague fear into a short conversation about who gets to be in the room. Requirement 6.4.3 is the reason you're allowed to say no. The roll call is how you find out who to say it to.