Article

Credential Stuffing: What It Is and How to Prevent It

Credential stuffing replays username and password pairs leaked from other sites' breaches against your login, betting on password reuse. Because each pair is tried once, per-account limits miss it — defend with per-IP rate limiting (HTTP 429), bot detection, and multi-factor authentication.

By Paul Rudenko, Security ResearcherUpdated Jul 1, 20267 min read

Credential stuffing is an automated attack that replays username and password pairs stolen from other websites' data breaches against your login page, betting that people reuse passwords. Because each pair is tried only once or twice, it slips past simple per-account limits — so the defence is layered: per-IP rate limiting that returns HTTP 429, bot detection at the edge, and multi-factor authentication as the backstop. This guide explains the attack and how to defend against it.

For the site owner (plain English)

When a big company gets breached, the leaked email-and-password lists end up for sale. Attackers know most people reuse the same password everywhere, so they take those lists and try them, automatically, against thousands of other sites — including yours. They are not guessing; they already have real passwords, and they are just checking which ones still work. This is credential stuffing, and it is one of the most common ways ordinary accounts get taken over. You cannot stop other companies getting breached, but you can make their leaks useless against your site: limit how fast anyone can attempt logins, challenge suspicious traffic, and turn on multi-factor authentication so a correct password alone is not enough to get in.

How credential stuffing works

The attacker feeds a tool a list of leaked email:password pairs, spreads the requests across many IP addresses (often a botnet or proxy pool), and submits each pair to your login endpoint. A small percentage succeed — the users who reused their breached password — and those accounts are then drained, resold, or used for fraud. The attack is high-volume but low per-account: each username might be tried only once, which is exactly why it evades a limit that only counts failures against a single account.

This is the mirror image of brute force, which throws many passwords at one account — see how to stop brute-force attacks. Both are automated password attacks, and both are addressed by the umbrella practice of rate-limiting your login page.

The layered defence

  • Per-IP rate limiting. Because per-account limits barely register a stuffing run, cap login attempts per IP over a short window and return 429 with Retry-After when exceeded.
  • Multi-factor authentication. The single strongest control: a correct stolen password cannot complete a login without the second factor.
  • Bot detection / CAPTCHA. Edge platforms and challenges (Turnstile, hCaptcha) distinguish automated tools from real browsers and block the volume.
  • Breached-password screening. Reject passwords known to appear in breach corpora at sign-up and reset (the Have I Been Pwned Pwned Passwords range API supports this without sending the password).
  • Anomaly monitoring. A sudden spike in login volume, failure rate, or new geographies is the signature of a stuffing run — alert on it.

This aligns with the OWASP Credential Stuffing Prevention Cheat Sheet, the primary reference for this attack.

Common gotcha: tuning your defence only around per-account failure counts. Credential stuffing tries each username just once or twice, so an account-only limit never trips — the run hides in the aggregate. You must also limit and monitor per IP and per the login endpoint as a whole, and rely on MFA for the accounts whose passwords do match.

Developer: per-IP limiting and screening

A per-IP throttle on the auth route is the minimum. Here it is in the common stacks; each returns 429 when the limit trips.

Node / Express — express-rate-limit

import rateLimit from "express-rate-limit";

const authLimiter = rateLimit({
  windowMs: 10 * 60 * 1000,
  limit: 30,               // per IP — catches high-volume stuffing
  standardHeaders: true,   // sends Retry-After
  legacyHeaders: false,
});

app.post("/api/auth/login", authLimiter, loginHandler);

Django — django-axes (per IP)

# settings.py
AXES_FAILURE_LIMIT = 20
AXES_COOLOFF_TIME = 1  # hours
AXES_LOCKOUT_PARAMETERS = ["ip_address"]  # per-IP catches the stuffing volume

Ruby / Rack::Attack (per IP)

# config/initializers/rack_attack.rb
Rack::Attack.throttle("auth/ip", limit: 30, period: 10.minutes) do |req|
  req.ip if req.path == "/api/auth/login" && req.post?
end

Java / Spring Security (per-IP block)

// Track attempts keyed by client IP, then in a filter:
if (attemptService.isBlocked(clientIp)) {
  response.setStatus(429);
  response.setHeader("Retry-After", "60");
  return;
}

Raw HTTP — the response

HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json

{"error": "too_many_requests", "retry_after": 60}

For the meaning of that status line and header, see the HTTP 429 Too Many Requests guide.

See whether your login endpoint limits high-volume requests — free, no signup.

Run the login rate limit checker

Read this alongside the pillar, how to rate-limit your login page, and the brute-force companion, how to stop brute-force attacks.

Frequently asked questions

What is credential stuffing?

Credential stuffing is an automated attack in which an attacker takes username and password pairs that leaked from one website's data breach and replays them, at scale, against the login pages of other websites. It works because a large share of people reuse the same password across multiple sites, so a fraction of the stolen pairs will still be valid somewhere else. The attacker is not guessing passwords — they already have real ones — so the goal is simply to discover which accounts on your site share a breached password. Each username is typically tried only once or twice, and the requests are spread across many IP addresses to look like ordinary traffic. A small percentage succeed, and those accounts are then taken over for fraud, resale, or theft. It is one of the most common causes of account takeover today, precisely because breached credential lists are cheap and widely available.

How is credential stuffing different from brute force?

Both are automated password attacks against a login page, but they invert each other. Brute force targets a single account and tries many different passwords against it — a dictionary, leaked password lists, or generated combinations — until one works; it is defeated by a per-account limit that refuses further attempts after a handful of failures. Credential stuffing targets many accounts and tries only one password against each: the real password that leaked for that user from another site's breach. Because each account sees just one or two attempts, a per-account failure limit barely registers the attack, which is what makes stuffing sneaky. The volume shows up instead across IP addresses and across the login endpoint as a whole, so per-IP rate limiting, bot detection, and anomaly monitoring are what catch it. Multi-factor authentication is the strongest defence against stuffing specifically, because it makes a correct stolen password insufficient on its own to complete a login.

Does rate limiting stop credential stuffing?

It helps, but on its own it is not enough, and the type of rate limiting matters. A per-account limit — the kind that blocks after several failures against one username — largely misses credential stuffing, because the attack tries each username only once or twice. What works is per-IP rate limiting combined with limits on the login endpoint as a whole, returning HTTP 429 with a Retry-After header when a source exceeds a sensible ceiling, so a high-volume run is throttled. But sophisticated attackers spread requests across large pools of IP addresses to stay under any single-IP limit, which is why rate limiting must be paired with other layers: bot detection or CAPTCHA challenges at the edge to distinguish automation from real browsers, anomaly monitoring to spot the traffic spike, and above all multi-factor authentication, which neutralises the attack even for the accounts whose passwords do match. Screening out known-breached passwords at sign-up further shrinks the pool of vulnerable accounts.

Related guides

See your whole external attack surface

One page is a start. The full external scan covers TLS, headers, DNS, exposed files, open services and known-exploited CVEs across your whole domain.

See the full scan →