8 Signs of a Payment Page Skimmer (And 3 False Alarms)

8 Signs of a Payment Page Skimmer (And 3 False Alarms)

A skimmed checkout does not look skimmed. That is the problem with the whole exercise. The signs of a payment page skimmer are mostly absent by design, because the code was written by someone who assumed you would open the page in Chrome, look at the network tab, see nothing odd, and go back to work. Orders keep arriving. Conversion does not dip. Nobody raises a ticket.

So this is triage, not a tour. The eight signs below are ordered by category, and within that by how much weight each one actually carries. Browser-visible signs come first because they are the ones you can check today, not because they are the most reliable. The signs that hold up in an argument with your acquirer live in file hashes, deployment history and server logs. Three findings at the end look like skimmers and are not. They cost teams half a day each.

Why a skimmed checkout usually looks completely normal

A payment page skimmer is JavaScript that does one of two jobs. It reads the card fields that already exist on the page and copies the values as they are typed or submitted. Or it draws a convincing card form of its own, collects the details, then hands the shopper on to the real payment flow so the order completes. Either way the values are posted to a collection host the attacker controls. The payment succeeds. So does the theft.

Current skimmers are built to survive inspection. The patterns are consistent enough to list:

  • The payload only runs when the URL matches a checkout, basket or payment path. Browse the homepage and the code sits inert.
  • It fires once per IP address or once per session, so a second look shows nothing.
  • It checks for an admin cookie or a logged-in staff session and skips those visitors entirely. The person most likely to test is the person least likely to trigger it.
  • It aborts if developer tools are open, detected through window dimension checks or timing on debugger statements.

Read plainly, that is a program written to defeat the exact inspection a merchant performs. “I looked and it seemed fine”is not a clean result. It is one negative test against an adversary who anticipated it.

Delivery route matters as much as payload, because each route leaves a different trail. Ticketmaster UK in June 2018 was skimmed through a compromised third-party supplier script, the chat widget on the payment page. Nothing in Ticketmaster’s own repository looked wrong, because nothing in it was wrong. The ICO fined the company £1.25m in November 2020. British Airways, over August and September 2018, was the opposite shape: code injected into a first-party script, which means file hashes and deployment history held the evidence. The ICO fine there was £20m, issued in October 2020. Then there is the platform route. Security firm Sansec reported in September 2020 that around 2,000 Magento 1 stores were skimmed over a single weekend, the same payload on unrelated sites, because the platform itself was unpatched and out of support.

Supplier route leaves network evidence. First-party route leaves file evidence. Platform route leaves both, plus company. Our breakdown of eight high-profile incidents across those three routes sets out the pattern in more detail.

One rule holds throughout: no single sign confirms anything. Two signs from different categories, say an unknown host in the browser and a modified timestamp on a payment template, is enough to treat the page as compromised and act accordingly.

Three signs you can see in the browser on your own checkout

These require no tools beyond a browser. They are also the signs a competent skimmer is most likely to hide from you.

Sign 1: a card field that should not exist in your own page

If your checkout uses hosted payment fields, the card number and CVV inputs belong inside the payment provider’s iframe. Script in the parent page cannot read inside that iframe. So attackers stop trying, and overlay a first-party form instead, or repoint the iframe source at something of their own.

The tell is structural. A CVV input in your own DOM on a page that uses hosted fields has no legitimate reason to be there. Nor does a duplicated card form, or an input that appears and accepts keystrokes before the provider’s iframe has finished loading. Inspect the elements and check which document they belong to.

Sign 2: a script, iframe or font request from a host you cannot account for

Open the checkout, load the network tab, filter to scripts and documents, and list every host. Lookalike domains are the thing to watch for: hostnames that borrow an analytics, CDN or payment brand name with an extra word, a swapped letter or a different top-level domain. They are chosen because they survive a glance down a list.

This sign is worthless without an inventory. More on that under the false alarms.

Sign 3: an outbound request when a field loses focus or the form submits

Click into the card field, click out, and watch the network panel. Then submit a test order with a card you control. A skimmer has to send the data somewhere, and exfiltration often rides on a request that looks innocuous: an image request to a host that has no business receiving anything, with the field values base64-encoded or URL-encoded into the query string. Decode the payload. If your postcode and expiry date are in it, you have your answer.

Our longer walkthrough of how to tell if your checkout has been skimmed covers these browser checks step by step, including what to do when the page behaves differently on a second load.

Three signs that only show up in your code, files and admin

These carry more weight than anything above, because they are harder for a payload to suppress and they survive the once-per-IP trick.

Sign 4: a changed hash or timestamp on a file nobody released

Compare file hashes against your last known-good build or your version control tree. Look at theme bundles, checkout templates and any core payment file. The classic shape is obfuscated code appended to the end of a legitimate JavaScript file, long after the licence header, often as a single dense line. Modification timestamps that do not line up with a deployment are the fastest way in.

Sign 5: injection points that are not files at all

Plenty of skimmers never touch the file system. On Magento, script tags live happily in CMS blocks, static block content, layout XML and widget definitions, all of which are database rows and none of which show up in a file integrity check. On WooCommerce, the equivalents are plugin settings, header or footer injection options and post content.

Check the adjacent evidence while you are there: admin accounts you did not create, API keys issued recently, scheduled tasks with odd command lines, and integration users with more scope than the integration needs.

Sign 6: supplier code that changed without a release note

A third-party script whose content changed when nothing was announced deserves a read. If you pin the file with Subresource Integrity, a failing hash is one of the sharpest signals available: the browser refuses to execute the file at all.

The cost is real, though, and it is worth stating. When a supplier legitimately ships an update, the hash breaks and the script silently stops loading. Teams under pressure respond by stripping the integrity attribute, which quietly deletes the tripwire they installed. Pin the script and agree a change-notification process with the supplier, or the pin will not survive its first false positive.

Two signs that reach you from outside, and why they arrive late

Sign 7: a common point of purchase report from your acquirer

This is the one that usually comes first. Issuers see fraud clustering on cards whose last common merchant was you, the schemes run common point of purchase analysis, and your acquirer calls. By then the code has been live for weeks. The call is not a warning. It is a finding.

Sign 8: policy violation reports and a run of customer fraud complaints

If you have a Content Security Policy in report-only mode, violation reports naming connect-src or script-src destinations you never approved are close to a direct accusation. The weaker companion signal is a rise in customers reporting card fraud shortly after ordering, which is noisy on its own but meaningful alongside anything else.

Both external signs lag the compromise, which has a practical consequence: your web server logs and deployment history need to reach further back than the lag. Thirty days of retention against a ten-week-old injection tells you nothing about scope. Ninety days is a more honest minimum.

Three false alarms that waste a day

False alarm 1: the tag marketing added two years ago

Most unrecognised scripts on a checkout are forgotten, not foreign. Consent managers, heatmaps, affiliate pixels, an A/B testing snippet from a trial that ended. An unknown script is only evidence if you hold an inventory good enough to separate foreign from forgotten. Without that list, every audit ends in the same inconclusive shrug.

False alarm 2: minified code that looks obfuscated

Minifiers mangle variable names, strip whitespace and collapse everything onto one line. That is their job. Dense, unreadable vendor JavaScript is normal. Worse, some legitimate fraud-scoring and device-fingerprinting libraries genuinely read form fields and post data to a vendor host, which is exactly the behaviour you are hunting. Check the host against your inventory and your contracts before concluding anything.

False alarm 3: noise from your own machine

Browser extensions inject script into pages. Password managers add fields and attributes to forms. Some antivirus and corporate filtering products intercept HTTPS and rewrite content. Audit in a clean profile with extensions disabled, or on a machine you have not customised. A surprising share of “we found a skimmer”calls end here.

What to do the moment a sign holds up

Preserve before you delete. Save the full page source, the response headers, a HAR file of the checkout session and hashes of the affected files. Export the relevant web server logs before rotation removes them. You will need all of it to tell your acquirer what was taken and for how long, and deleting the payload first destroys the only copy of the answer.

Scope the window from evidence, not from the date you noticed. Deployment history, file modification times and requests to the collection host in your access logs give you a start date. Your own discovery date gives you nothing except the end.

Then close the route rather than the payload. Removing injected code from a template while an attacker still holds an admin password buys you a few days. Rotate admin credentials and API keys, patch the platform, review integration users, and if the code arrived inside a supplier file, raise it with the supplier before you assume it is gone. The first 24 hours after a checkout compromise have a particular order to them, and preservation sits at the top of it.

Making the next skimmer announce itself sooner

Requirements 6.4.3 and 11.6.1 of PCI DSS 4.0 have been mandatory since 31 March 2025, and they are published by the PCI Security Standards Council. Compliance aside, they describe the two things that make detection possible at all.

Requirement 6.4.3 asks for an inventory of every script on the payment page, with a written justification for each and a method of authorising it. That list is the reference every browser-side check depends on. Build it once and “an unknown script”becomes a sign instead of a question. Our guide to what 6.4.3 and 11.6.1 ask of your payment page sets out what counts as adequate justification.

Requirement 11.6.1 asks for change and tamper detection on the payment page. A report-only Content Security Policy with a collection endpoint, plus a scheduled hash comparison of your payment page HTML and its script bundles, covers it for the cost of setup time. Report-only mode blocks nothing, so it cannot break checkout while you tune it.

Reduce the reach as well. Moving card entry into hosted payment fields stops parent-page script from reading the values directly, and it is still the cheapest structural fix available. Be clear about the limit, though. It does not stop an overlay form, it does not stop an iframe being repointed, and it does not stop your page being modified. It changes the shape of the evidence rather than removing the risk.

The short version

Browser checks are useful and weak. File hashes, database injection points, supplier diffs and logs are slower and stronger. The first sign most merchants get is a phone call from an acquirer about fraud clustering weeks old, which is why retention and deployment history decide how well you can scope it. Forgotten tags, minified vendor code and your own browser extensions account for most false positives. And the central point stands: a modern payment page skimmer is written to show no signs to whoever is checking, so a clean look is a single negative test, not a verdict.

Frequently Asked Questions

Can a payment page skimmer be invisible to shoppers?

Yes, and that is the design goal. The code copies the card details and allows the payment to complete normally, so the order confirmation arrives and nothing on the page behaves oddly. Shoppers usually learn about it when their bank contacts them, weeks later.

What are the first signs of a payment page skimmer a merchant usually notices?

Most often it is an external report: a common point of purchase notification from the acquirer, or issuers flagging fraud on cards last used with you. Internally, the earliest technical signs tend to be a modified file timestamp that does not match a deployment, or a Content Security Policy violation report naming a destination nobody approved.

Does using hosted payment fields or a payment iframe remove the risk?

It removes the easiest attack and keeps card data out of your own page, which is why it is worth doing. It does not stop an attacker overlaying a convincing card form on top of the iframe, repointing the iframe source, or otherwise modifying your page. Treat it as a reduction in reach, not an exemption from monitoring.

How long do skimmers typically sit on a checkout before anyone spots them?

Published incident timelines vary widely, from days to several months, and the honest answer is that detection usually depends on outside reporting rather than the merchant. Since the external signal lags the injection by weeks, assume your log retention and deployment records need to reach back further than you expect.

Which checks satisfy PCI DSS 4.0 requirements 6.4.3 and 11.6.1 without buying a tool?

A maintained, justified inventory of payment page scripts covers the substance of 6.4.3, and a report-only CSP with a violation endpoint plus scheduled hash comparison of the page and its bundles addresses 11.6.1. Both need documenting, reviewing and actually reading, which is where unmanaged setups fail. Confirm the detail with your acquirer or assessor, since reporting expectations differ by SAQ type.