How to Detect a Skimmer on My Website in an Hour

How to Detect a Skimmer on My Website in an Hour

A skimmer on a live checkout does not break anything. That is the problem. Working out how to detect a skimmer on my website is not a matter of looking for errors or slow pages, because a competent skimmer leaves the order flow exactly as it was. The card is charged, the confirmation email sends, the customer is happy for about three weeks. What follows is a timed audit you can run today, in a browser you already have, before anyone asks you to buy a monitoring product.

Set aside an hour. Use a real checkout on the live site, not staging, and be ready to place and refund a genuine low-value order. A skimmer that fires on submit will not show itself if you never submit.

What a skimmer actually looks like in a live browser

Strip away the branding and every client-side skimmer does three things. It attaches a listener to the fields where card data is typed. It reads the values out of those fields. It sends them somewhere that is not your payment provider.

Find any one of those three and you have found something worth investigating. Find the third and you are almost certainly looking at an active theft. The mechanics of how card details are copied at the moment of payment are unremarkable JavaScript; the skill is all in the hiding.

And they do hide, deliberately. Modern skimming code is conditional. It checks the URL and only runs when the path contains something like /checkout or /onepage. It waits for a card field to lose focus, or for the submit handler, so the data is complete before it is copied. It sets a cookie or a localStorage flag after a single execution, so the same browser never sees the code again. Some check for an open DevTools window and go dormant. Some geofence, running only for visitors in a particular country. Some skip anyone carrying an administrator session cookie, which means the person most likely to look is the person least likely to see it.

Exfiltration comes in two shapes. The obvious one is a POST to an attacker-controlled domain, often a lookalike of your own brand or of a well-known CDN. The quieter one is a GET for an image that never renders, a WebSocket, or a base64 blob padded with plausible-looking analytics keys so the request map reads like a marketing tag.

One thing to discard now: a clean server-side malware scan of your homepage tells you very little. Scanners walk files. The skimmer may never be in a file, and it may never load anywhere except the payment page.

The 60-minute checkout audit

Minutes 0 to 15: record a real transaction

Open a fresh browser profile with no extensions. Open DevTools, go to the Network tab, tick “Preserve log”and “Disable cache”. Then walk the full journey: product page, basket, checkout, card entry, submit.

Now sort the requests by domain. Write down every third-party host that appears on the pages where card data is entered, and put a name against each one. Not a guess. A name: this is the payment provider, this is the analytics vendor marketing signed off in March, this is the review widget. Anything you cannot attribute to a person or a contract goes on a shortlist.

Pay attention to what fires after you submit. That is where the interesting requests live.

Minutes 15 to 35: trace the initiator, compare the source

The Initiator column tells you which script caused each request. Hover it and you get the call stack. A tag that loads another tag that loads a third script from a host nobody recognises is the single most common structure in a supplier-route compromise. Ticketmaster UK found that out in June 2018, when the skimming code reached its customers through a compromised chat widget supplied by Inbenta. The merchant had approved the widget. Nobody had approved what the widget later loaded.

Then take the first-party scripts, the ones served from your own domain, and open them in the Sources panel. Compare what the browser received against what is in your repository or your last known-good build. This is the check people skip, and it is the one that would have mattered to British Airways between August and September 2018, where the modified code sat in a script the business owned. The ICO issued a final penalty of £20 million in that case. The file was theirs. The JavaScript was not.

Minutes 35 to 50: read the code for tells

You are not reverse-engineering anything. You are reading for shapes.

  • addEventListener bound to inputs whose names or ids reference card, cc, cvv, expiry or the payment form itself
  • Long chains of atob, String.fromCharCode, hex arrays or character-code maps that assemble a string at runtime
  • fetch, XMLHttpRequest or navigator.sendBeacon pointing at a host you did not list in minutes 0 to 15
  • Domains that almost match something real: an extra hyphen, a swapped letter, a.cc or.su tail on a name borrowed from a CDN or from your own brand
  • Code that reads document.location and compares it to a checkout path before doing anything else

Obfuscation on its own is not proof. Plenty of legitimate vendors minify and pack. Obfuscation plus a card-field listener plus an unfamiliar outbound host is not a coincidence.

Minutes 50 to 60: change the conditions

Run the whole thing again with the conditions altered. Switch DevTools to a mobile user agent. Use a different network, a phone on mobile data or a VPN exit in another country. Use a browser profile that has never visited the site and is not logged in as an administrator.

This last quarter-hour is the part most people leave out, and it is why a single clean pass proves nothing. If the code fires once per visitor and you already burned your one shot, the second look is genuinely clean. The site is not.

Malicious JavaScript that never appears in your files

File integrity monitoring is useful and it misses an entire class of injection, because the code is not on disk. On Magento it lives in CMS blocks, CMS pages, layout XML updates, theme configuration or the design settings. On WordPress and WooCommerce it lives in the options table, in a widget, in a plugin setting that accepts HTML. Diff every file on the server and you will report a clean site while the skimmer loads from a database row.

Tag managers deserve their own paragraph. A GTM container is code deployment with no code review, and it is frequently owned by marketing rather than engineering. If a container can publish arbitrary JavaScript to the payment page, then anyone with access to that account, including anyone who has phished a login from an agency, can deploy a skimmer without touching your infrastructure at all.

Half an hour on the server is worth spending too. Compare deployed files against a clean build. List templates modified in the last 90 days. Check administrator accounts for names nobody can place, check cron entries for anything fetching a remote URL, and check the platform version. In September 2020, security firm Sansec reported roughly 2,000 Magento 1 stores compromised over a single weekend, all of them running an end-of-life platform. A published route into an unpatched system needs no cleverness at all. Our breakdown of eight high-profile incidents and the three routes in sets out how consistently these entry paths repeat.

Content Security Policy in report-only mode as a detector

Most guidance treats CSP as a blocking control and stops there. Used in report-only mode it is a detection instrument, and an unusually good one.

Serve Content-Security-Policy-Report-Only on the payment page with a deliberately tight script allowlist and a reporting endpoint. Nothing is blocked. Nothing can break a live transaction. What you get back is a log of every script origin the browser actually loaded, from every real customer, on every device and network you could never test yourself. Origins nobody on the team remembers approving show up within hours. Build the allowlist from what you catalogued in the audit, then treat each violation report as something a human reads, not a counter on a dashboard. A working CSP setup for a checkout page takes an afternoon.

Subresource Integrity is the next step, and it is worth being honest about the cost. Pinning a supplier script to a hash genuinely prevents a tampered version from executing. It also means that when the supplier ships a perfectly legitimate update, the hash stops matching and the script silently disappears. To the business, your security control broke checkout. Agree an update process with the vendor before you pin anything on the payment path, or you will be asked to remove the control the first time it works.

Hosted payment fields and provider iframes remove the card data from your DOM entirely, which removes the classic listener-and-copy attack. They do not remove everything. A script on your page can still draw a convincing fake form over the top, or replace the iframe wholesale. The parent page still matters.

Outside signals that find it before you do

In a large proportion of real cases, the first reliable evidence comes from outside. An issuer or acquirer runs a common point of purchase analysis, sees a cluster of fraudulent cards that share one merchant, and calls you. Customers report duplicate payment forms, a card form that looks slightly wrong, or an error after entering their details followed by a successful second attempt.

Browser warnings, hosting abuse notices and threat-intelligence reports naming your domain belong in the same bucket. Treat any of them as a live incident from the moment it arrives. Waiting for an internal scan to confirm it will cost you weeks, because the scan will keep coming back clean for all the reasons above. Our guide on how to tell if your checkout has been skimmed goes through those external signals in more detail.

From one audit to continuous payment page change detection

An hour tells you about one hour. Since 31 March 2025, PCI DSS 4.0 has required rather more than that, and two requirements do the heavy lifting.

Requirement 6.4.3 asks for an inventory of every script on the payment page, each one authorised, each one justified in writing, each one confirmed for integrity. Requirement 11.6.1 asks for a mechanism that detects and alerts on unauthorised modification to the HTTP headers and content of the payment page, evaluated at least every seven days or on a frequency set by a documented targeted risk analysis. Both are in the standard published by the PCI Security Standards Council, and neither one names a product. If you need a practical route through both requirements, a UK merchant’s 30-day defence plan sets out the audit and controls week by week.

You can meet both cheaply. A CSP report endpoint that somebody reads. SRI hashes on the scripts you can pin. A scheduled headless fetch of the payment page that stores the script list and content hashes and diffs them against yesterday, alerting on change. A script register in a spreadsheet or repository, tied to your change control, showing who approved each tag and why. That combination is the honest floor, and our note on what 6.4.3 and 11.6.1 ask of your payment page sets out the evidence an assessor expects.

Commercial client-side monitoring earns its cost when you run many payment pages, when scripts change weekly, or when nobody has time to triage CSP reports. If you have one checkout and one engineer, it largely duplicates what you can build in a day.

You found a skimmer: the first 48 hours

Preserve first. Copy the malicious code, the file timestamps, the database rows, the web and access logs, and the CSP reports, before anyone deletes anything. Rushing to clean destroys the evidence that tells you the entry route and the exposure window.

Then contain. Remove the injection, rotate administrator credentials, API keys and integration tokens, patch the platform and the path that was used, and re-audit the whole checkout rather than assuming the single artefact you found was the whole job. Reinfection within days is common, and it almost always means the original access remained open.

On obligations: tell your acquirer and payment provider promptly, because card scheme timelines start from their notification and a forensic investigator may be mandated depending on your transaction volume. Assess the incident under UK GDPR and, where the personal data breach meets the risk threshold, report it to the ICO within 72 hours of becoming aware. Document the decision either way.

The short version

Run the hour with DevTools on a live checkout, submit a real order, and name every third-party host. Repeat on mobile, on a second network, in a clean profile, because skimmers are conditional and one clean pass proves nothing. Look in the database as well as the files. Put CSP in report-only mode on the payment page and read what comes back. Then keep doing it weekly, which is both good sense and, since 31 March 2025, what PCI DSS 4.0 expects.

Frequently Asked Questions

Can a standard malware scanner detect a skimmer on my website?

Sometimes, and not reliably. Server-side scanners compare files against signatures, so they miss injections stored in the database, code delivered through a tag manager, and malicious script hosted on a third-party domain you load legitimately. A scanner that only crawls your homepage will also never see code that executes solely on the checkout URL.

What are the clearest signs of a payment page skimmer?

An outbound request from the payment page to a domain that is not your payment provider, analytics vendor or CDN is the clearest single sign. Others include event listeners bound to card fields, runtime string assembly using atob or character codes, lookalike domains, and duplicate or oddly styled card forms reported by customers. A cluster of fraud reports tied to your store is often the first real evidence of all.

How do I check for malicious JavaScript on my checkout without breaking it?

Every check described here is read-only. DevTools observation, source comparison against your repository and a report-only Content Security Policy all record behaviour without blocking a single request. Keep blocking controls, including SRI and an enforced CSP, for a planned change window rather than an investigation.

Does using hosted payment fields mean I no longer need to check for skimmers?

No, though it removes the most common attack. With card data inside the provider’s iframe, a script on your page cannot read it directly. It can still overlay a fake form, swap the iframe for its own, or capture the name and address fields that sit outside the frame, so the parent page still needs monitoring.

How often does PCI DSS 4.0 require payment page change detection?

Requirement 11.6.1 calls for the detection mechanism to be evaluated at least once every seven days, or at a frequency justified by a documented targeted risk analysis. It has been mandatory since 31 March 2025, alongside requirement 6.4.3 on script inventory and authorisation.