How to get alerted when your checkout page loads a new script

Magecart skimmers arrive as one new or altered script on your payment pages. How to detect that within minutes with browser-sourced monitoring via the CSP header, alerts on new script origins and unjustified payment-page scripts, sent to Slack, Teams, email or your SIEM. No agent on the page.

Published · Updated

To get alerted when your checkout page loads a new script, make every visitor’s browser report which scripts it executes (the Content-Security-Policy header does this natively, via hash reporting), feed those reports into a script inventory, and put alert rules on top: one for a script origin your visitors have never loaded from, and one for any script that shows up unjustified on the pages you declared as payment pages. The moment either happens, the rule fires on ingest into Slack, Teams, email or a webhook. Detection within minutes, no crawler, no agent on the page.

That is the whole architecture. The rest is why each piece matters and how to tune it.

Why a monthly script review finds the skimmer a month late

The Magecart playbook has barely changed in a decade: compromise one script that already runs on the checkout (a chat widget, an analytics tag, a self-hosted bundle via a compromised CDN), or inject one new one, exactly on the pages where card numbers are typed. The skimmer looks like every other third-party script. It does not crash anything. Conversion stays flat.

Which is why the quarterly or monthly “review the scripts on checkout” ritual fails. British Airways’ skimmer ran for about two weeks. Plenty of smaller incidents run for months. If your detection cadence is a calendar entry, your mean time to detect is half the review interval on a good day, and in practice worse, because the reviewer is comparing against a spreadsheet that was already stale.

A crawler-based scanner does better but still misses two things: skimmers that only serve to real users (fingerprinting the headless browser is standard practice now), and the gap between crawl runs.

What good detection looks like on a payment page

Three properties, in order of importance.

Browser-sourced. The requirement is to know what real customers’ browsers executed, because that is where the card data is. A skimmer injected by a compromised CDN never touches your servers, so server-side file integrity monitoring is blind to it.

Scoped to payment pages. A new script on the blog is a Tuesday. A new script on /checkout/payment is an incident until proven otherwise. Detection that treats both the same trains your team to ignore it.

Minutes to the on-call channel. Not a dashboard someone checks, not a weekly digest. The delta between “new hash observed” and “human looking at it” should be one Slack notification long.

A fourth property gets underweighted: the detection mechanism should not itself add a script to the checkout. Several client-side security products work by putting their own JavaScript agent on the payment page. That means another third party on the exact page you are protecting, and that vendor pulled into your PCI scope conversation. I would rather monitor the page without touching it.

Setting it up with CentralCSP

CentralCSP’s Script Inventory is built from CSP hash reporting (report-sha256 and friends under the script directives): every script that loads on your pages arrives in the inventory, per page, with its origin, full URL, SHA-256 hash and hash history, first and last seen. One HTTP header to deploy, nothing new executing on the checkout, and the inventory is on every plan. We wrote up the mechanics in how to detect third-party script changes.

On top of the inventory, alert rules give you two layers.

The first is site-wide and comes with Business (€129.99/mo): a new script origin rule, which fires the moment a hostname that has never served your visitors a script does so. It is the loudest possible skimmer signal and the one most injection routes trip, because attackers rarely bother hosting the payload on a domain you already trust. It also ignores your own deploys by construction: app.3f2a91.js becoming app.8bc410.js on the same origin is not a new origin.

The second is page-level and comes with Scale (€349.99/mo), where the PCI DSS module lives. You declare your payment pages (/checkout/*, the pages hosting your payment iframe), the module inventories their scripts from real traffic, and each script gets authorized with a written justification. Anything new on those pages afterwards, a new URL or a known URL serving a hash that no auto-validation rule covers, lands in a pending queue, is written to a dated change timeline, and fires the unjustified script on a payment page rule. That is the tamper case, the one a URL allowlist cannot see, and the one 11.6.1 was written for.

Delivery goes to Slack, Microsoft Teams, Google Chat, Telegram, email, or HMAC-signed HTTPS webhooks whose JSON drops into Splunk, Datadog, PagerDuty, Opsgenie or Jira without an adapter (webhook-compatible, not native connectors). A rule can fan out to up to 20 channels, alerts are unlimited, rules evaluate on ingest rather than on a schedule, and every delivery attempt sits in a history with retries. Start (€39.99/mo) has the inventory and no alerting, which is fine for building a policy but not for guarding a checkout.

Tuning: every alert should be worth waking for

Scope tightly. The temptation is to watch the whole site “while we’re at it”, and the result is a channel nobody reads by week three. The payment-page rule is the one that pages, through the webhook to PagerDuty or into the on-call Slack channel. The site-wide origin rule can go to a quieter channel, or to email, and be read in the morning.

Then kill the false positives from your own releases and your vendors’. Your bundler’s content-hashed filenames never trip the origin rule. On the payment pages, an auto-validation rule for a vendor’s routine hash rotation (the chat widget that ships weekly on its usual URL) keeps it out of the pending queue, while a hash change outside any pattern you set still queues and still pages. On top of that, every rule has a cooldown (15 minutes by default) and anything else that fires inside it is batched into one message rather than dropped. After those filters, in our experience, what remains is either a marketing team adding a tag without telling anyone (worth a conversation) or an actual incident (worth the page).

The PCI DSS angle

PCI DSS v4 requirement 11.6.1 mandates a change and tamper detection mechanism on payment pages, evaluating the page and its headers as received by the consumer browser, at least once every seven days. Continuous browser-sourced detection does not just meet the weekly minimum, it makes the minimum look like what it is: a floor written for merchants doing manual checks. The alert trail doubles as detection evidence. The full PCI evidence tooling (justification workflow, per-page inventory, dated change timeline, CSV and PDF export for the assessor) is the same Scale module the payment-page rule belongs to, and its records are kept until the account is deleted, not for the 90 days the raw reports live. The details are in our 6.4.3 and 11.6.1 breakdown.

If you take one thing from this: the attack is one new or altered script on a known set of pages. That is a small, precise thing to watch for. Watch for it continuously, from the browser’s side, and make the alert land where your on-call already lives. CentralCSP does exactly that from one header: script inventory on every plan, the site-wide origin rule from Business, payment-page rules and evidence from Scale, and nothing added to your checkout. Alerting is one piece of a checkout setup. What your checkout actually needs covers the rest of it.

Frequently asked questions

How do I get an alert when a new script appears on my payment page?

Use the Content-Security-Policy header to make every visitor browser report which scripts execute on the page, feed those reports into a script inventory, and put two alert rules on top: a new script origin rule on the site, and on the Scale plan, the unjustified-script rule on the pages you declared as payment pages. A script from an origin your visitors have never loaded from fires the first, any script or hash change on a payment page that nobody has justified fires the second, both on ingest, into Slack, Teams, Google Chat, Telegram, email or a signed webhook.

Can I detect Magecart without adding a monitoring agent to my checkout?

Yes. Browsers can report the URL and SHA-256 hash of every script they execute through CSP hash reporting. That signal comes from one HTTP header, so nothing new runs on the payment page. Adding a vendor JavaScript agent to a checkout is itself a supply-chain and PCI-scope decision, which is exactly what you are trying to avoid.

How do I stop script alerts firing on every legitimate deploy?

Two levers. The new script origin rule ignores your own bundle renames by construction: app.3f2a91.js becoming app.8bc410.js on the same origin is not a new origin. On declared payment pages, the PCI module lets you set auto-validation rules for the known patterns (a vendor library rotating its hash on its usual URL), so routine rotations never reach the pending queue, and each rule carries a cooldown that batches whatever fires in the same quarter hour into one message. A genuinely new script still fires.

Does new-script alerting satisfy PCI DSS 11.6.1?

Requirement 11.6.1 demands a tamper-detection mechanism on payment pages running at least every seven days. Continuous browser-sourced detection exceeds that minimum by a wide margin. QSA acceptance of the evidence package varies, so pair the alerts with an exportable script inventory and change timeline. Payment page monitoring, script justification and the evidence export sit on the Scale plan (€349.99/month) and up.