Gift cards are the only thing you sell that is already money. Nothing ships, nothing is named, and the balance moves the moment a code is known. That changes the attacker's problem: they are not trying to buy something and keep it, they are trying to turn stolen inputs into a clean, resellable balance.

Three attacks account for almost all of it, and they need different answers.

Draining: the balance-check endpoint is the target

Your balance checker is usually a small form that takes a card number and a PIN and answers instantly. No login, no rate ceiling worth the name, and a response that differs depending on whether the card exists. That is an oracle, and it is the softest endpoint most retailers own.

The attack is unglamorous: run millions of candidate numbers through it, keep the ones with money on them, and sell those. The cards were never stolen physically. They were guessed.

What makes it work is that the endpoint is cheap to call and honest in its answers. Two fixes follow directly. Make the response uniform — the same shape and the same timing whether a card exists, has a balance, or does not exist at all. And put an infrastructure check in front of it, because a campaign that has to run at human pace from residential addresses costs a great deal more than one that runs from a rented box.

curl -X POST https://maskbreak.com/api/lookup \
  -H 'Content-Type: application/json' \
  -d '{"ip":"185.220.101.1"}'
{
  "ip": "185.220.101.1",
  "known": true,
  "verdict": "block",
  "risk_score": 90,
  "signals": { "vpn": false, "proxied": false, "tor": true, "dch": false, "anon": true },
  "network": { "asn": null, "org": "Tor exit node", "country": null, "city": null }
}

Enumeration: why per-IP limits do nothing here

Every retailer that has been hit tries per-IP rate limiting first, and it fails for the same arithmetic reason it fails against scrapers. A residential proxy pool spreads the guessing across thousands of home connections making a handful of requests each. No threshold you can set survives contact with that without also breaking the customer checking a card they were given at Christmas.

The signal that survives rotation is the device. A card-checking run driven by one machine through a thousand addresses is still one machine, and the fingerprint does not rotate because the address did. Count attempts against the device identifier, not the IP, and the arithmetic reverses: the attacker now needs a thousand real machines instead of a thousand addresses, which is a different budget entirely.

// Count on what the attacker cannot cheaply change.
const v = await sentinel.evaluate({ token: req.body.sentinelToken });

const attempts = await db.countRecent('giftcard_check', v.device?.visitor_id, '1 hour');
if (attempts > 20) {
  return res.status(429).json({ error: 'Too many balance checks.' });
}
if (v.decision === 'block') {
  return res.status(403).json({ error: 'Unavailable from this network.' });
}

Note the order. The velocity check runs on the device before the network verdict, because a patient attacker on a clean residential address is exactly the case the network layer will not catch.

Laundering: buying cards with stolen cards

The third attack runs in the other direction. Rather than draining your cards, the attacker buys them with a stolen payment card, because a gift card converts a card that will be charged back into a balance that will not be. Your chargeback arrives six weeks later; the code was resold on the day of purchase.

This is the one where the fraud check has to sit at purchase, not redemption, and where the useful signals are the ordinary ones — a datacenter or tunnelled connection at checkout, a device that has bought gift cards under several different accounts, a billing country that has never matched this device before.

The tell that separates it from ordinary card testing is the basket. Card testing wants the cheapest possible authorisation. Gift card laundering wants the highest denomination that will clear, often several at once, and it does not care about anything else in your catalogue.

Where to put the checks

Three surfaces, in the order they are worth doing.

  • Balance check. The highest-volume, lowest-friction target you own, and the one that is usually completely unguarded. Uniform responses plus a device-keyed velocity counter.
  • Gift card purchase. Where laundering is stopped, and where a review verdict should cost the buyer a verification step rather than the sale.
  • Redemption. Worth logging, rarely worth blocking. By redemption the money has usually already moved, and the person holding the code is often the innocent recipient of a resold card.

That last point is the one teams get wrong. Blocking at redemption feels like enforcement and mostly punishes a customer who bought a card in good faith from a marketplace. The decision belongs earlier, where the attacker is, not later, where the victim is.

Keep the recipient out of it

Gift cards are given to people who are not your customers yet. They arrive with no account, no history, and no patience, often on a phone, often on a shared network. Anything that treats an unfamiliar device as suspicious by default will land hardest on exactly the person you most want to convert.

Route on the decision rather than the score: block the unambiguous, add a step for the uncertain, and let everything else through untouched. A first-time device on a residential connection with no velocity behind it is the normal case, not the risky one.

What this does not solve

None of this recovers balances already drained, and none of it helps if your card numbers are sequential or your PINs are short. Guessability is a design problem, not a detection problem: enough entropy in the code space makes enumeration uneconomic before any of this runs. Detection buys you the time to fix that, and catches the campaigns that come anyway.

FAQ

Frequently Asked Questions

Why does rate limiting the balance-check endpoint not stop gift card draining?

Because rate limiting assumes the address is scarce and a residential proxy pool makes it abundant. The run spreads across thousands of home connections at a few attempts each, which sits under any threshold you can set without breaking real recipients checking a card they were given. Count attempts against the device identifier instead, which does not rotate when the address does.

Should I block gift card redemption if the risk score is high?

Usually not. By redemption the money has generally already moved, and the person holding the code is frequently an innocent buyer who got a resold card from a marketplace. Blocking there punishes the victim rather than the attacker. Put the decision at balance check and at purchase, and treat redemption as something to log rather than refuse.

How is gift card laundering different from card testing?

Card testing wants the cheapest authorisation that proves a stolen card works. Gift card laundering wants the highest denomination that will clear, often several at once, and ignores the rest of your catalogue. Both arrive at checkout with a stolen card, but the basket shape is the tell, and laundering converts a chargeback-able card into a balance that is gone the same day.

Does a uniform balance-check response really matter?

Yes, and the timing matters as much as the body. If a valid card with a balance answers differently, or faster, than a card that does not exist, the endpoint is an oracle and enumeration becomes a search problem with feedback. Make the shape and the latency identical across all three outcomes before you tune anything else.

Put a check in front of the balance endpoint

One call returns VPN, proxy, Tor and datacenter signals plus a device identifier that survives IP rotation. Free at 1,000 requests an hour, no card.

Get a free API key