Credential stuffing is the quietest attack most teams face. There is no exploit, no malformed payload, no obvious spike in traffic. Someone takes email and password pairs from an older breach, points a bot at your login endpoint, and waits for the small percentage of people who reused a password. Every request looks like a customer signing in.
The economics favor the attacker. An attempt costs almost nothing, and one account takeover pays for thousands of failures. That is why the defense is not a single clever rule but a stack: identity checks, velocity limits that do not depend on a single IP, and a plan for what happens after a login succeeds.
Credential stuffing is not brute force
Teams often confuse the three main password attacks, and the mix-up leads to the wrong controls. Brute force guesses many passwords against one account, so account lockouts and complexity rules help. Password spraying tries a handful of common passwords across many accounts, so broad lockouts help there too.
Credential stuffing is different: the username and password are already correct on some other service. Your password policy is irrelevant, and lockouts mostly punish your real users. Per-IP rate limits fail because the traffic arrives from thousands of addresses at once.
How the bots avoid tripping your alarms
Modern stuffing tooling is built around looking unremarkable. The operators know the same detection tricks defenders do, and they design around them.
- Residential proxy pools. Traffic comes from real consumer ISPs, so IP reputation, geolocation and ASN checks stay quiet. Datacenter-only blocklists miss it entirely.
- One attempt per account, spread over hours. Low volume per credential means per-account velocity counters never fire.
- Fingerprint repair. User agent, accept headers, TLS handshake, HTTP/2 settings, screen size and canvas output are aligned so the client looks like Chrome on Windows or Safari on iOS.
- Real sessions. Many bots load the login page, execute JavaScript, wait a beat, then submit. Some outsource hard challenges to cheap human or solver services.
- Direct API calls. Mobile app login endpoints often lack the protections bolted onto the web page, and they return clean JSON instead of HTML.
- Traffic shaping. Attempts are timed to peak hours and scaled to your normal login volume, so the attack never stands out on a dashboard.
- Cross-site testing. One list gets tried against dozens of targets at once, so no single service sees more than a trickle.
Detection signals worth instrumenting
You cannot block what you cannot see. Capture more than the username, password and source IP on every login attempt, and treat these patterns as risk inputs rather than hard bans.
- Failure skew. Login POSTs that fail far more often than they succeed, especially when the ratio shifts suddenly.
- Account-to-device mismatch. Dozens of distinct usernames attempted from one device fingerprint or one subnet in a short window.
- Known-breached passwords. The username is valid and the submitted password appears in a public breach corpus. You can check this with a k-anonymity style lookup or a local hash set without storing plaintext.
- Fingerprint contradictions. The user agent claims an iPhone but the TLS handshake, header order or missing client hints say otherwise.
- No journey to the form. A login POST with no preceding page load, no referrer, or session cookies that are absent while the CSRF token is freshly generated.
- Robotic timing. Near-identical intervals between requests across thousands of sessions. People are jittery; scripts are not.
- Post-login behavior. A successful sign-in followed within seconds by a password change, new email address, new API key or altered payout details.
A failed login is not a neutral event. Ten thousand of them in an hour is a signal, and one success buried inside them is an incident.
Mitigation that survives rotation
Because the attack is distributed, defenses have to be built around identity and behavior, not just addresses. A workable set of layers:
- Add a second factor, and prefer passkeys. Credential stuffing ends at a phishing-resistant factor. WebAuthn or passkeys are strongest; push approvals with number matching are a reasonable middle ground. Avoid SMS where you can.
- Rate limit by identity, not IP. Track attempts per account across all source addresses, per device fingerprint, per ASN and per endpoint. Use a gradually tightening limit instead of a fixed cap that attackers can sit just underneath.
- Escalate challenges instead of blocking. A risk score should route sessions: allow silently, run a JavaScript or device check, present a CAPTCHA, demand step-up verification, then block. Blocking first produces false positives and support tickets.
- Check credentials against breach data at signup and at login. If a match appears, require a reset with step-up verification and explain why to the user.
- Require a real session to log in. Reject login POSTs that skip the page, the challenge script or the expected cookie and token sequence.
- Deploy honeypot accounts and hidden form fields. No legitimate user touches them. Any interaction is a reliable, low-noise alert.
Protect the account after the door opens
Some takeovers will succeed. Plan for that. Flag sessions created after a risky login and watch them closely for a short period, since attackers move fast to secure access by changing recovery email, enrolling their own MFA device or creating API credentials. Notify the user on any change to those settings, on a new device, and on sign-in from an unfamiliar location.
Give users a way to see active sessions and revoke them, and make password resets invalidate existing sessions. On the support side, treat a sudden wave of "I can't log in" tickets as a possible attack signal, not just a support backlog.
A practical checklist for the next sprint
- Log device fingerprint, ASN, TLS fingerprint, timing and token state on every login attempt.
- Turn on breached-credential checks with a forced reset path.
- Roll out passkeys for admins and high-value accounts before the general user base.
- Add per-account velocity limits that do not reset when the source IP changes.
- Require a valid session and challenge completion before accepting a login POST.
- Set alerts on honeypot accounts and on post-login changes to recovery settings.
- Rehearse. Run a controlled test with a small list of dummy accounts and confirm your alerts fire where you expect.
None of this requires a big-budget platform, and most of it can be staged. Start with visibility, then add friction where the risk is highest. The bots will keep changing addresses and fingerprints. The constants are the accounts they are chasing and the behavior they cannot hide, so build your defenses around those.
Photo: Brett Sayles / Pexels