Guide

Credential stuffing: why your login looks fine and still leaks

The attack that succeeds without ever tripping a rate limit — what it looks like in your logs, why the usual controls miss it, and what actually catches it.

The attack, and why it is not brute force

Credential stuffing does not guess passwords. It replays them. Someone takes username and password pairs from a breach of an unrelated service and tries each pair once against your login, betting on the fact that a meaningful share of people reuse credentials.

That single difference breaks most of what you have deployed. Brute force is loud: many attempts against one account, easy to rate-limit, easy to lock out. Stuffing is one attempt per account across thousands of accounts, spread over hours and rotating addresses.

The success rate is low — commonly quoted between a tenth and a few percent — and that is entirely sufficient. Against a list of a million pairs, a tenth of a percent is a thousand working accounts.

And the successful login is, by every technical measure available to your application, correct. The password was right. There is nothing to reject.

What it looks like in your logs

The reason this goes unnoticed for weeks is that no individual signal looks alarming. The pattern only exists in aggregate.

  • Failure rate, not failure count. Your absolute number of failed logins may barely move, but the ratio of failures to successes shifts — because the attempts are spread across accounts that mostly do not exist or do not match.
  • One attempt per account. A rate limiter keyed on the account never fires. A limiter keyed on the address fires only if the attacker forgot to rotate.
  • Address distribution. Attempts arrive from residential proxy pools, which means addresses that look like ordinary customers rather than like a datacentre.
  • Timing that is too even. Human login traffic follows your customers' daily rhythm. A stuffing run is flat across the night.
  • A quiet success spike from unfamiliar addresses. This is the one that matters and the one nobody alerts on, because a successful login is not an error.

Why the usual controls miss it

Each of these is worth having and none of them stops this attack on its own.

Rate limiting is keyed on the wrong thing. Per-account limits never trigger at one attempt per account. Per-address limits are defeated by rotation through a residential proxy pool, which is a commodity service.

Account lockout is worse than useless here and actively dangerous. It cannot trigger at one attempt, and on a public login it lets anyone disable your customers' accounts on demand.

CAPTCHAs are a per-attempt cost the attacker already budgeted. Solver services price them per thousand, and at a 0.1% success rate the economics still work comfortably.

Password complexity rules do nothing. The password being replayed is already valid — it satisfies whatever rules the user's other service imposed, and probably yours.

Multi-factor authentication is the one control that genuinely breaks the attack, and it is worth every effort to deploy. It also does not help on the accounts that have not enrolled, which on a consumer product is most of them.

What actually catches it

Because the attack is invisible in any single request, the detection has to work at the level of the session and the address.

Session-level signals. A stuffing run automates the form: fields populate without the timing of typing, focus moves in a way a human hand does not produce, the submission arrives faster than a person could plausibly have filled it. This is the same behavioural layer any invisible bot detection reads, and it is visible on the very first attempt.

Transport fingerprints. The tooling has to make a TLS connection, and its stack shows. A JA3/JA3N fingerprint that does not match the browser the session claims to be is a strong signal, and the attacker cannot fix it by editing a header.

Reputation across attempts. One attempt per account gives you nothing per account, but the same address making one attempt against four hundred different accounts is unambiguous. That aggregate only exists if something is scoring the address rather than the request.

Cross-tenant reputation. Stuffing lists are replayed against many sites in the same campaign. An address that ran a list against someone else's login last night is worth stopping before it starts on yours — which is only possible if the knowledge is shared.

A defensible setup

Ordered by how much of the outcome each step decides.

Deploy MFA and drive enrolment. Nothing else on this list breaks the attack outright. Make it the default for new accounts and prompt existing ones after a successful login from a new address.

Alert on successful logins from unfamiliar addresses. This is cheap, it uses data you already have, and it is the signal that a stuffing run has started to work.

Score the session, not the request. Behavioural and transport signals catch the automation on the first attempt, before the aggregate pattern has had time to form.

Enforce at the gateway. A decision applied in page JavaScript is a decision the attacker's tooling never runs.

Check credentials against known breaches at set and at login. If the password being replayed is already in a public corpus, you can refuse it before it becomes anyone's problem.

Keep your allow list current. Your own QA automation and load tests look exactly like this attack, and blocking them on a release day is its own kind of incident.

Where Karma sits

Karma covers the third and fourth items: it scores each session from behavioural and transport signals, keeps a per-tenant reputation base so one address across many accounts is visible as one actor, and offers an opt-in shared blocklist so a list being replayed across the industry is known before it reaches your login.

The verdict goes to your gateway, which stops the request before your application ever evaluates the password — so the attempt costs you nothing and never appears as a successful login you have to investigate.

It does not replace MFA, and this page is not suggesting it does. MFA is the control that makes a replayed password worthless; everything here is about stopping the traffic that is testing which passwords still work.

FAQ

What is credential stuffing?
Replaying username and password pairs stolen from a breach of another service against your login, betting on password reuse. It is not password guessing: each pair is tried once, across thousands of accounts, so per-account rate limits and lockout policies never trigger and the successful logins are technically valid.
Why doesn't rate limiting stop credential stuffing?
Because it is keyed on the wrong thing. Per-account limits never fire at one attempt per account, and per-address limits are defeated by rotating through a residential proxy pool, which is a commodity service. The pattern only becomes visible when something scores the address across attempts rather than counting requests.
How do I detect credential stuffing in my logs?
Look for the ratio rather than the count: failures rising against successes, one attempt per account across many accounts, timing that stays flat overnight, and addresses from residential pools. The alert that matters most is on successful logins from addresses with no history — the failures are noise, the successes are the breach.
Does MFA stop credential stuffing?
Yes, for enrolled accounts — a replayed password is worthless without the second factor, which makes MFA the single most effective control. It does nothing for accounts that have not enrolled, which on a consumer product is usually most of them, so it belongs alongside session-level detection rather than instead of it.