Payment pages
Magecart and card skimming
A skimmer is JavaScript on your checkout page reading the card fields and sending a copy somewhere else. It does not need a vulnerability in your payment processor, and it leaves the transaction working perfectly, which is why these run for weeks.
The browser is the only place that can see it happen. This page is about what it reports when it does, and — as bluntly as we can put it — about the case where it reports nothing at all.
What the attack is
"Magecart" started as the name of one group and is now shorthand for the technique: get JavaScript onto a page that handles card data, read the fields as they are typed, and post them to a server you control. The code is usually small, often obfuscated, and frequently written to fire only on URLs containing checkout or payment, which keeps it off the pages a developer is most likely to have open.
There are two ways it arrives, and they are not equally visible. Either an attacker adds a new script to your page — through a compromised vendor, a tag manager, or an injection — or they modify a script that was already there, on an origin you already trust. The first violates a tight script-src. The second does not violate anything.
What both have in common is the last step. The stolen data has to leave, and it leaves over the network, to a host that is not yours. That is the part a policy can be written against.
What the report looks like
A payment page with a connect-src naming only the origins it genuinely talks to produces this the first time a skimmer tries to send anything:
{
"csp-report": {
"document-uri": "https://shop.example.com/checkout/payment",
"referrer": "https://shop.example.com/checkout/delivery",
"violated-directive": "connect-src",
"effective-directive": "connect-src",
"original-policy": "default-src 'self'; script-src 'self' https://js.stripe.com; connect-src 'self' https://api.stripe.com; report-uri https://ingest.headerhawk.com/r/abc123",
"disposition": "report",
"blocked-uri": "https://cdn-payments.example.net/collect",
"status-code": 200,
"script-sample": ""
}
}How it appears in HeaderHawk
connect-src blocking cdn-payments.example.net
- document-uri is the page the request came from. A connect-src issue whose pages are only the payment step is a very different thing from one spread evenly across the site, which is almost always a globally loaded SDK.
- blocked-uri is where the data was going. Browsers sometimes report only the origin rather than the full path for a cross-origin connection, so the value you see may be shorter than the URL the code used; grouping is by host either way.
- connect-src covers fetch, XMLHttpRequest, sendBeacon and WebSocket. A skimmer that exfiltrates by setting an image src instead reports under img-src, so a payment page worth protecting wants both named.
- original-policy is stored with every report, so you can tell a genuinely new host from one you removed from the allowlist last Tuesday.
- disposition: report means the request went through. Report-only mode is a microscope, not a filter.
Field by field: the connect-src reference.
What HeaderHawk does with it
Reports arrive grouped by the underlying problem: one row per directive and blocked host, carrying its report count, the number of distinct pages it appeared on, and first- and last-seen timestamps. For a skimmer the first-seen timestamp is the interesting one — it is approximately when the code went live, which is the first thing an incident timeline needs and the hardest thing to reconstruct afterwards.
Opening the issue gives the individual reports: the exact page, the source file and line the browser attributed the request to, the policy in force, and the browser and OS. The source file is what tells you which script made the call, which is the difference between knowing you are being skimmed and knowing what to remove.
Alerting is on browser agreement. Violations accumulate in 15-minute windows; a window reported by two or more browser families, or carrying twenty or more reports from one, sends to Slack, email or a webhook straight away, with a deduplication window — 24 hours by default — so an ongoing problem alerts once rather than every quarter hour. Quieter windows go into a daily or weekly digest. Extension and bot traffic is classified as noise first and does not trigger anything.
On the Team plan and above, the Scripts view separately lists the SHA-256 hashes of scripts browsers actually executed, which is the other half of the picture — what ran, rather than what a policy would have refused. It is a sample and it raises no alerts of its own; the limits are below, and the PCI DSS 4.0 page goes through them in the context of requirements 6.4.3 and 11.6.1.
Where CSP alone is not enough
A page about card skimming is the easiest page on this site to overclaim on, so this list is the part to read first.
- A script allowlist does not catch a modified first-party file. This is the single most important limit on this page. The British Airways skimmer lived inside a file BA already served from its own origin; script-src 'self' allowed it, because it was 'self'. Subresource integrity and a build pipeline you trust are what address that, not an allowlist.
- Report-only mode prevents nothing. It tells you what an enforcing policy would have refused. The block, when there is one, is your own header doing it — HeaderHawk is never in the request path and never sees your visitors' traffic.
- There is no alert when a new script appears. Executed-script hashes are collected for inspection, and only the violation path raises notifications. If you want to be paged the moment a bundle changes, this is not that.
- An exfiltration endpoint you already allow produces no report. A skimmer that posts to your own origin, or to an analytics host already on your connect-src, violates nothing. Tightening connect-src on the payment page specifically is what makes this signal sharp.
- The script inventory is a sample, not a census. Hash reports come from Chromium 133 and above, cover only scripts the browser actually fetched — never cached or inline ones — and repeat sightings are sampled. First sightings get priority rather than a guarantee: over quota or over the daily per-site ceiling, a new hash waits for the next sighting. The view starts on the Team plan.
- It does not monitor your response headers. If your CSP stops being served — a misconfigured cache rule, a rolled-back deploy — nothing here turns red. Reports simply stop arriving, which looks like a quiet week.
- None of this is a PCI DSS control on its own, and nothing here makes anyone compliant.
Where to go next
- PCI DSS 4.0 maps all of this onto requirements 6.4.3 and 11.6.1, including which halves of them nothing here addresses.
- The
connect-srcreference covers what the directive does and does not cover — including the navigations it never sees. - Third-party scripts is the other arrival route: a vendor's code changing without telling you.
Watch what your checkout talks to
Add a report-only connect-src to the payment step and see every origin it reaches. Nothing breaks while you look.