How Magecart skimmers show up in CSP violation reports
Card skimmers arrive as a new script, a modified one, an inline handler or a fake payment form, and send data to lookalike hosts. Which CSP report each variant produces, and which produce none.
Michael Cameron6 min read
Magecart started as the name for one group skimming Magento checkouts and is now the word for the whole technique. JavaScript on a page that handles card data reads the fields as they are typed and sends a copy somewhere else. Sansec, which tracks these campaigns, counts over 70,000 stores that have carried a skimmer at some point, and more than 100,000 once supply-chain victims are included. RiskIQ and Flashpoint profiled seven distinct groups in 2018.
The British Airways and Ticketmaster posts each cover one incident. Across incidents, a skimmer makes the same four moves, and each either produces a CSP report or does not.
Getting onto the page
There are four ways in, and they are not equally visible.
A new script from a host the attacker controls, injected through a compromised
platform, a tag manager or a stored XSS. Under a policy that names the scripts
the page is allowed to load, that is a script-src-elem violation with the
attacker’s host in blocked-uri, on the first page view that loads it.
A modification to a script that is already there. British Airways’ skimmer was
22 lines appended to a Modernizr copy on the airline’s own origin. Nothing about
loading it changed, so script-src 'self' allowed it and no report was sent.
A modification at a vendor. Ticketmaster’s skimmer lived in a chat widget served from Inbenta’s servers, at a hostname the page had to allow for the widget to work. To the policy this is the first-party case again: the file was allowed because its host was.
An inline handler. Sansec found a campaign in April 2026 that
hid its loader in an SVG element’s onload attribute
across 99 stores. An inline handler is a script-src-attr violation under any
policy without 'unsafe-inline', and with 'report-sample' the report carries
the first part of the handler’s code.
Reading the fields
This move makes no noise. The skimmer binds to the submit button, or to every button and form on the page, and serialises the inputs when it fires. RiskIQ’s description of the Ticketmaster skimmer is representative: any button or form is hooked, and on a click or submit the fields’ names and values are collected and sent. CSP does not govern reading the DOM, so no directive fires here.
A variant skips reading the real form and draws its own. Malwarebytes documented
a skimmer in 2019 that
injected an iframe posing as a payment provider’s card form
onto Magento checkouts, loaded from a domain registered in Russia, then showed
a fake error and forwarded the customer to the real processor to enter their
card again. In 2023 it described
the Kritec skimmer’s modal forms,
which used the compromised store’s own logo and were served from a loader
script named after the store. The rogue iframe is a frame-src violation with
the attacker’s host in blocked-uri. The modal built by an already-loaded
script is not a violation at all.
Looking like something that belongs
The hosts skimmers talk to are chosen to survive a glance. British Airways’
data went to baways.com. Ticketmaster’s went to webfotce.me, a typo of
“webforce”, and RiskIQ noted the group’s earlier drop servers had mimicked
JavaScript libraries and analytics services. The polyfill.io payload loaded
its second stage from googie-anaiytics.com. Sansec’s SVG campaign sent data to
six domains that all used the path /fb_metrics.php, dressed as a Facebook
analytics endpoint.
A report undoes this move. blocked-uri is the host as the browser saw it, and
a host is either on the policy or it is not. baways.com looks like British
Airways to a person reading a network tab in a hurry. To a connect-src that
lists four origins, it looks like nothing.
Leaving
The data has to travel, and it travels over the network. A fetch, an
XMLHttpRequest or a sendBeacon call reports under connect-src. An image
request with the fields encoded into its URL reports under img-src. A form
submission reports under form-action. A skimmer that reaches a host the page
does not otherwise talk to produces a report under whichever of those it used,
carrying the page, the source file the browser attributed the request to, and
the host.
What the issues look like
HeaderHawk groups reports by effective directive and blocked host, so the variants above arrive as separate issues, each with a report count and first- and last-seen timestamps. Opening one lists the reports with the source file and line, the browser and operating system, and the policy that was in force.
| Move | Issue title |
|---|---|
| New script from attacker host | script-src-elem blocking loader.example.net |
| Inline handler | script-src-attr inline event handler (a1b2c3d4) |
| Rogue iframe | frame-src blocking thatispersonal.com |
| Exfiltration by fetch or XHR | connect-src blocking webfotce.me |
| Exfiltration by image request | img-src blocking webfotce.me |
| Modified script, allowed host | none |
A campaign that rotates drop domains opens a new issue per domain, each with its own first-seen timestamp, a fair picture of what happened. The hash in the inline title is of the code sample, so two different inline handlers are two issues.
Alerting is on browser agreement. Reports for an issue accumulate in 15-minute windows, and a window reported by two or more browser families, or carrying 20 or more reports from one, goes to Slack, email or a webhook, then repeats no more often than the rule’s window, which defaults to a day. Reports whose blocked URI is a browser extension, or whose user agent is a known crawler or headless browser, are classified as noise on arrival and never count toward a window.
Where the reports stop
Every report above depends on the skimmer touching a host the policy does not name, and the groups know it. Sansec described a 2020 skimmer that ran entirely on Google’s infrastructure, its loader on Firebase storage and its card data leaving as custom events to a Google Analytics account the attacker owned, and put it that CSP is close to worthless once Google Analytics is on the allowlist. An allowlist is only as safe as its hosts, and the analytics and tag-manager hosts on a typical checkout page are the wide door.
A modified script on an allowed host reports nothing when it loads: British Airways, Ticketmaster and most of the serious cases. Subresource integrity and a build you trust are the controls for that.
A skimmer that keeps the data on the server, in one of Sansec’s cases appended to an image for the attacker to collect later, never crosses the network from the visitor’s browser, and nothing is reported.
Enforcing mode refuses the requests that reported, and only those. It is your own header, set once the reports have shown what the allowlist should be.
Sources
- Sansec, What is Magecart?, and SVG Onload Tag Hides Magecart Skimmer on 99 Stores, 7 April 2026, and Digital skimmer runs entirely on Google, defeats CSP, 22 June 2020.
- RiskIQ and Flashpoint, Inside Magecart: Profiling the Groups Behind the Front Page Card Breaches, 13 November 2018.
- RiskIQ, Inside and Beyond Ticketmaster: The Many Breaches of Magecart, 11 July 2018, via the Internet Archive.
- Malwarebytes Labs, Skimmer acts as payment service provider via rogue iframe, 21 May 2019, and Magecart threat actor rolls out convincing modal forms, 27 April 2023.
- W3C, Content Security Policy Level 3, for which directive governs each request.