Attack Patterns

Why Card-Testing Attacks Bypass Velocity Rules and How to Catch Them Earlier

Diego Ramirez 8 min read
Abstract visualization of card-testing attack patterns

Card testing is one of those attack patterns that looks obvious in hindsight. A bot cycles through a stolen card list, making small transactions or authorization checks to find which cards are live. The fraud shows up in your chargeback queue a few days later, and someone pulls the velocity data and says: "look, 47 attempts in 90 seconds from the same IP." Clear as day.

The problem is that by the time the velocity spike looks obvious, the attack is already over. The useful cards have been identified. The bot is gone. What you caught was the exhaust, not the fire.

We have spent a lot of time on this problem, both because it keeps coming up in pilot conversations and because it was the original motivation for building Fraudpulsar. Velocity rules are not bad. They are just optimized for a version of this attack that sophisticated operators stopped using in 2022 or so. Here is what the current landscape looks like.

How modern card-testing bots actually behave

Early card-testing bots were blunt instruments. Volume attacks against a single merchant, a small number of source IPs, rapid-fire in bursts. Velocity rules built in 2019-2020 were calibrated for exactly that shape, and they worked.

What operators do now is closer to mimicry. The distribution of attempts per IP stays below your threshold. Transaction amounts cluster in a range that looks like real micro-purchases: $1.00, $1.50, $0.99. Timing intervals are randomized within human-plausible ranges. Some bots rotate through residential proxy pools, so each attempt comes from a different IP in a different city.

Against a velocity rule that asks "did this IP attempt more than 5 transactions in 10 minutes," this is invisible. The rule fires on none of these attempts individually. It fires in aggregate only if you look across multiple IPs at the same merchant during the same window, which most velocity rule implementations do not do because it is expensive to compute in real time.

The BIN-country match check also fails here. Cards are tested against merchants that operate globally, so a card issued in Germany being tested by a proxy in the Netherlands looks entirely plausible.

What the signal looks like before the burst

Here is what we actually see in the data before a card-testing burst becomes obvious:

Low-value authorization attempts that fail on a specific merchant but not others. A bot probing a single acquiring endpoint will produce a cluster of failed authorizations on one merchant ID that does not correspond to any historical pattern for those card BINs. If you have per-account and per-merchant baselines, this stands out immediately. If you only have global velocity counts, it is noise.

Transaction timing that is too regular. Human transactions have entropy in their timing: you buy something, think about it, add another item. Bot timing has a characteristic interval distribution, often clustering around 200-500ms between attempts or at whatever delay the operator set in the script. We have seen some more sophisticated operators introduce random noise here, but the distribution still looks different from human behavior once you have enough samples to compute a KL divergence or similar measure against the per-account baseline.

Device fingerprint recycling across accounts. A card-testing operation cycling through a stolen list will often use the same client-side fingerprint against multiple account IDs in the same session window, unless they are rotating headless browser profiles. This cross-account correlation is only visible if your system is looking at fingerprints across account boundaries in real time, not just within a single account session.

Amount clustering in ranges that are not organic. Real micro-transactions on a payment platform have a distribution shaped by what people actually buy. Card-testing transactions are designed to be small enough not to trigger holds, so they cluster in a narrow band: under $2 for card-brand authorization checks, or under $5 for soft-decline probes. If your baseline shows this account historically makes purchases averaging $35-80 and suddenly there are three $1.00 attempts, that deviation is meaningful even before any velocity threshold fires.

Where velocity rules actually help

We are not saying velocity rules are useless, because they are not. They catch the blunt attacks that still run, which is a non-trivial volume of low-sophistication fraud. They are fast to compute and easy to audit, which matters for compliance reviews and chargeback disputes where you need to explain why a transaction was blocked.

Velocity rules are also good at catching operators who get sloppy. A human running a card-testing script who gets impatient and cranks up the rate will still trip a well-configured velocity check. Rules work on the long tail of unsophisticated attacks.

Where they fail specifically is on the well-resourced operators who have studied your rules. If your velocity threshold is 5 attempts per IP per 10 minutes, and that is a publicly known threshold (or easily discoverable by probing), then any operator who matters will stay at 4. The rule becomes a rate limiter, not a detector.

A scenario that illustrates the gap

Consider a digital payments platform with a card-linking feature: users add a card to their account to enable one-click payments. One afternoon, 340 new account registrations come in over about 3 hours. Each account attempts to link a card. Most fail with a soft decline. Each account uses a different IP from a residential proxy pool.

Velocity rules fire on nothing. No single IP exceeds 2 attempts. No single account has unusual activity. Global authorization failure rate ticks up slightly, but not enough to break a threshold tuned to avoid false positives on legitimate link-and-fail patterns (which are common: users mistype card numbers, enter expired cards, etc.).

What a behavioral model sees: 340 accounts with no prior transaction history, all created in a 3-hour window, all attempting a card-link action within minutes of registration, all with authorization failures, all with device fingerprints that cluster into about 8 distinct client profiles despite appearing to come from different IPs. The behavioral deviation score on each of these accounts, relative to the baseline of new accounts that successfully link cards, is extremely high.

That is the signal that catches the attack at hour one instead of in the chargeback queue four days later.

How we approach detection in practice

The Fraudpulsar scoring model looks at card-testing risk from two angles that complement the velocity check: per-account behavioral deviation and cross-account pattern correlation.

Per-account deviation compares the current transaction's characteristics against the specific account's historical baseline. For a new account, the baseline is sparse, so we fall back to cohort-level priors: what does a legitimate new account registration look like on this platform versus what does a testing operation look like. The behavioral features that matter most for card testing are amount distribution, authorization failure rate, timing interval entropy, and merchant category consistency.

Cross-account correlation is a layer that runs asynchronously against the account population in a session window. When we see multiple accounts sharing device fingerprint clusters or exhibiting the same timing distribution pattern within a short window, that is a signal that goes back into each account's risk score even if each account individually looks below threshold.

The output is a 0-1 score per transaction with contributing factors flagged. A risk team looking at a blocked card-link attempt sees which factors pushed the score: "new account, no prior transactions, 6 shared fingerprint cluster matches in the last 2 hours, authorization failure on first attempt." That is actionable for both blocking and for your chargeback dispute documentation.

The threshold calibration problem

One thing worth flagging: even with a good behavioral signal, you still have a threshold calibration problem. Set your block threshold too low and you will stop legitimate new customers who had the bad luck to register during an attack window and happen to share an IP subnet with the bot traffic. Set it too high and you are back to catching the attack only after it has run.

The way we handle this is with a three-tier output: block (high confidence fraud), review (score above baseline but not high enough to auto-block), and pass. The review queue lets a human take a second look at accounts that scored high but were not auto-blocked. On a well-tuned deployment, review queue volume is low: fewer than 1 in 200 transactions flagged for manual review under normal traffic conditions, based on what we have seen in pilots.

Card-testing attacks also tend to be time-bounded. The operation runs, the usable cards are identified, and the traffic stops. This means that even if some accounts slip through the review queue and are not resolved immediately, the window of attack is finite. The damage is in the verified cards that were not blocked. Minimizing that set is the goal, and it requires catching the attack in real time, not in the chargeback queue.

More from the blog