Brute-Force Attack
What is a Brute-Force Attack?
A brute-force attack finds a secret by trying candidates until one works. In its pure form it tries every possible combination, which is why the term is often used loosely for any guessing attack.
The distinction that matters in practice is between attacks against a live service and attacks against stolen data, because the defences are completely different.
Online versus offline
| Online | Offline | |
|---|---|---|
| Target | A running login endpoint | A stolen password hash database |
| Rate limited by | Your service, and the network | The attacker's own hardware |
| Realistic rate | Tens to thousands per second, if undefended | Billions per second on consumer GPUs |
| What defends it | Rate limits, lockouts, human verification | The hash function: bcrypt, scrypt, Argon2 |
This is the single most useful thing to understand about the topic. Once an attacker has your hash database, nothing you do at the login endpoint matters — only the choice of hashing algorithm does. And at the login endpoint, the strength of the password matters far less than whether you limit attempts.
The variants
- Pure brute force tries every combination. Viable only against short or low-entropy secrets.
- Dictionary attack tries likely passwords first, from wordlists of previously breached ones. Vastly more efficient, because password choice is not uniformly random.
- Credential stuffing is not really brute force at all: it replays known-correct pairs from other breaches. Success rates in the low percentages, against billions of available pairs.
- Password spraying tries a handful of very common passwords across many accounts, specifically to stay under per-account lockout thresholds.
That last one defeats the obvious defence. Account lockout counts failures per account, and spraying makes few attempts per account across thousands of accounts, so no counter ever trips.
Defending the login endpoint
- Hash properly. Argon2id or bcrypt with a sensible work factor. This is the only defence that survives a database breach.
- Rate limit on the right key. Per-IP limits are defeated by residential proxies and punish shared addresses; per-account limits are defeated by spraying. Limit on both, plus the total failure rate across the endpoint, which is what actually reveals a distributed attack.
- Prefer slowing to locking. Account lockout is a denial-of-service primitive an attacker can aim at your users. Increasing delay, or requiring verification after repeated failures, achieves the defensive purpose without that side effect.
- Verify a human before checking the credential. This is the cheap win: a distributed attack dies before it reaches the password comparison, which also keeps the attacker from learning which pairs are valid.
- Make attempts cost. Proof of work prices each attempt in CPU time, which is negligible for one login and prohibitive across millions.
And remove the reason to guess at all where you can — WebAuthn has nothing guessable in it.