8

PricingBlog
All posts
engineering

Compromised ≠ Down: Why a Hacked Magento Store Can Show 100% Uptime

2026-09-23 · Byte8 Team

Share

The worst day a Magento store can have often starts with a perfect status page. Uptime: 100%. Response time: 380ms. Zero failed checks, zero alerts, a wall of green. And a skimmer copying every card typed at checkout, or a webshell sitting in pub/media waiting for its next instruction.

This is the uncomfortable truth about a hacked store: being compromised and being down are two completely different things, and almost every monitoring tool is built to notice only the second one. A store that's been breached is, in the overwhelming majority of cases, still serving customers flawlessly — because staying up is the whole point of the attack. This post is about why "up or down" is the wrong model, why a compromise hides inside a green dashboard, and why we decided a compromised store needs a status of its own.

Compromised vs down: what's the difference?

Down is an availability failure. The store can't do its job because it isn't responding: a 500, a timeout, a connection refused, a maintenance page that never lifts. Downtime is loud. It pages you, it shows up as a red bar on the uptime chart, and everyone agrees it's a problem.

Compromised is an integrity failure. The store is responding perfectly — every request returns a healthy 200 OK — but it's doing something it shouldn't: running injected JavaScript, serving a webshell, exposing an attacker-created admin account, or exfiltrating data. The store's *availability* is fine. Its *trustworthiness* is gone.

The two are orthogonal. A store can be up and healthy, up and compromised, down and clean (a bad deploy), or — worst case — down and compromised. Collapsing all of that into a single up/down axis throws away the one distinction that matters most when you've been breached: the store is working, and that's exactly why nobody noticed.

Why a compromised Magento store still shows 100% uptime

An uptime monitor asks one question — *did the page respond?* — and a compromised store answers "yes" all day long. Here's why every conventional signal stays green:

  • The attacker wants you online. A skimmer only earns money while customers are checking out. A webshell is only useful while the server is reachable. A store that goes down gets investigated; a store that stays up gets left alone. Uptime is the attacker's friend, not yours.
  • The malicious code is cheap. A few lines of injected JavaScript add no measurable latency. The page still loads in 400ms, so response-time monitoring and APM see nothing unusual.
  • Nothing throws. The injected code is designed to run silently. No PHP exception, no 500, no spike in the error-rate graph — so error-based alerting never fires.
  • The order still completes. Even a synthetic checkout that drives a real browser will finish the purchase, because a skimmer rides *alongside* checkout rather than breaking it. The transaction succeeds; the card just gets stolen on the way.
Put those together and the uptime SLA — the number a lot of teams treat as the health of the store — can read a clean 100% for the entire duration of a breach. Worse than useless: it's actively reassuring while the store bleeds.

The two-state model is the bug

Most monitoring tools model a store as a light switch: on or off. That model is fine for a static marketing site, where "responding" really does mean "working." It falls apart for a transactional Magento store, where the interesting failures aren't about whether the server answers — they're about *what it answers with*.

We've written before about why a fast store can still be broken — a 200-but-dead checkout, a drifted indexer serving empty category pages. A compromise is the sharpest version of the same problem, because it's not an accident you can fix with a redeploy — it's an adversary who wants to stay hidden. If your monitoring only has two states to describe the world, a breach has nowhere to live except "up," and "up" is where it wants to be.

The fix isn't a cleverer uptime check. It's a richer status model — one that can say "this store is available *and* it is compromised" as a single, coherent fact.

What "compromised but up" actually looks like

These are all fully-online failures. Every one returns 200, every one keeps the store trading, and none of them is visible to an uptime or performance dashboard:

what's wrong                     store up?   throws?   uptime sees it?
------------------------------   ---------   -------   ---------------
injected card skimmer (CMS/db)   yes         no        no
webshell dropped in pub/media    yes         no        no
attacker-created admin account   yes         no        no
config tampering (payment/URL)   yes         no        no
malicious cron / injected task   yes         no        no
supply-chain script swap (CDN)   yes         no        no

The common thread: each lives in the store's *content, config, or filesystem*, not in its availability. You can't fetch the homepage and infer any of them. You have to read what Magento is actually running and serving — core_config_data, CMS blocks, the admin user table, the files in pub/media — and compare it against what should be there. That's a different kind of monitoring from "ping the URL every minute." (For the mechanics of one of these — how injected JavaScript actually harvests cards — see our teardown of a Magento card skimmer.)

Why we made "compromised" a first-class status in Pulsar

When we built Pulsar, the two-state model bothered us for exactly this reason: a skimmed store is *up*, and treating "up" as "healthy" would mean the tool actively lied to you during the single worst moment it exists to catch. So compromised is its own status — separate from up, separate from down, and deliberately kept off the uptime SLA.

That last part matters. A compromise doesn't quietly drag your uptime number down to 99.2% and hope you notice — it surfaces as its own state, in its own colour (a fuchsia badge, not the red we use for downtime), because it's a *different class of problem* that needs a different response. Downtime means "fix the server." Compromised means "you have an intruder" — patch, clean, rotate secrets, assume breach. Merging the two would blur the one signal that should trigger an incident, not a restart.

Underneath the status, Pulsar's content-integrity collector reads the places compromises actually hide — core_config_data, CMS content — and flags injected <script> tags, unexpected external script domains, and known skimmer signatures. It runs continuously, so an injection added an hour ago is caught at the next check rather than at the next manual audit. It's one of 23 collectors that read Magento's own internal state, the layer an external check can't reach.

A note on honesty, because this category is still maturing: content-integrity monitoring catches the common, database-resident injection vector very well. A payload loaded through a compromised third-party script lives outside your store entirely, which is why watching your external script sources — and running a Content-Security-Policy — matters alongside it. No single check is a silver bullet; the point is that "compromised" should be a state your monitoring can *name*, so the failure has somewhere to show up.

What to do when your store is compromised but up

If you suspect a breach while the store is still trading, the instinct to "just keep it running" is the trap — every hour up is more stolen data. Work the problem in this order:

Confirm the integrity failure, not just availability

Don't trust the uptime page. Read the actual content and config: query core_config_data for <script> you didn't add, audit CMS blocks for unfamiliar external src domains, list the scripts the checkout page loads, and diff your templates against clean git. Check the admin user table for accounts you don't recognise.

Contain before you clean

Assume admin credentials are compromised: reset every admin password and enforce 2FA with no exceptions. Revoke integration tokens and API keys created during the breach window. If the compromise reached the filesystem, treat app/etc/env.php — database credentials and the encryption key — as stolen and rotate them.

Remove at the source

Redeploying from clean git fixes file-level injections but not database-resident ones. Remove the injected core_config_data row, CMS block, or template edit directly, and check both the codebase and the database.

Close the door and watch it

Apply outstanding Magento security patches, add a Content-Security-Policy to constrain which domains can run scripts and receive data, and — the part that turns this from a recurring nightmare into a caught event — monitor content integrity continuously, so the next injection surfaces in minutes rather than at the next quarterly audit or the next chargeback.

Common questions

Can a Magento store be hacked and still show 100% uptime? Yes — and it usually does. A compromise is an integrity failure, not an availability one. The store keeps responding with healthy 200s, pages stay fast, and no exception is thrown, so uptime monitoring, response-time monitoring, and error-rate alerting all stay green. The attacker *wants* the store online, because a skimmer or webshell only pays off while the site is reachable.

What's the difference between "compromised" and "down"? Down is an availability failure: the store can't respond. Compromised is an integrity failure: the store responds perfectly but is serving injected code, exposing an attacker's admin account, or exfiltrating data. They're independent — a store can be up and compromised at the same time, which is the most dangerous combination because nothing looks wrong.

Why doesn't my uptime SLA catch a compromised store? Because an SLA measures whether the store responded, and a compromised store responds fine. The breach doesn't cause downtime, so it never subtracts from the uptime number. A store can report a flawless 100% uptime for the entire duration of a skimmer campaign — which is why a compromise needs its own status rather than being folded into the availability metric.

How do you detect a compromise if the site looks healthy? By reading the store's own state instead of just fetching the page: check core_config_data and CMS content for injected scripts, list and verify every external script the storefront loads, audit admin accounts and integration tokens, and diff templates against clean git. Continuous content-integrity monitoring automates this so an injection is caught at the next check rather than the next manual audit.

Should a compromise page me like downtime does? It should alert you — but as its own kind of incident, not as "the server is down." The response is different: downtime means fix availability; compromised means contain an intruder, clean the injection, and rotate secrets. That's the whole argument for treating it as a first-class, separate status.

A hacked Magento store rarely announces itself with an outage. It hides behind the exact green dashboard you built to prove everything's fine. "Is the site up?" simply can't answer "has someone tampered with it?" — those are different questions on different layers. Give a compromise a status of its own, watch the store's integrity and not just its availability, and the breach finally has nowhere left to hide.

See how Pulsar monitors Magento →


Enjoyed this? Share it with your team.

Share