← Blog

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

Read your own reports this way

Point your policy's reporting at HeaderHawk and see violations grouped by directive, blocked source and page. The Free plan covers 1 site.