15.8. Brute-Force Protection
Table of Contents
Understanding Brute-Force Attacks
Brute-force attacks are attempts to guess secrets by trying many possibilities until one works. In backend systems this almost always means:
- Guessing passwords on login forms
- Guessing reset tokens or verification codes
- Guessing API keys or access tokens
The attacker automates requests and keeps trying. If you do not detect and slow them down, they can send thousands or millions of attempts.
Typical brute-force patterns:
- Many failed logins for the same account
- Many failed logins from the same IP
- Many attempts against a password reset endpoint
- Many tries of one-time codes (SMS, email, authenticator app)
You must design your backend so that these attacks are detected, limited, and expensive for attackers, while still being usable for real users.
Brute-force protection is not optional on any login, password reset, or token-based endpoint that is exposed to the internet.
In this chapter we focus on protection strategies, not on basic authentication concepts, which are covered elsewhere.
Threat Models and Targets
Online vs Offline Brute Force
There are two major brute-force scenarios:
Online brute force
The attacker sends requests to your live backend:
- Target: login, password reset, 2FA, signup (for abuse), API key guessing
- Limitations: network speed, rate limits, your protections
- Example: 1000 login attempts per minute from a botnet
This chapter focuses mainly on online brute-force protection.
Offline brute force
The attacker gets a copy of password hashes or tokens and tries to crack them offline:
- Target: your stored credentials (database dump, log leak)
- Limitations: your password hashing algorithm, attacker's hardware
- Example: attacker has
password_hashfrom DB backups and runs a GPU-based cracker
Offline brute-force resistance is mainly achieved by secure password hashing (covered in another chapter), but your online protections still matter because they reduce the chance that attackers can obtain credentials in the first place.
Common Attack Targets
Typical endpoints that need protection:
| Endpoint type | Examples |
|---|---|
| Login | /login, /auth/token, /api/auth/login |
| Password reset | /password/forgot, /password/reset |
| Account recovery / unlock | /account/recovery, /unlock |
| 2FA / OTP verification | /2fa/verify, /otp/check |
| Signup (abuse control) | /register, /signup |
| Token verification | /verify-email, /magic-link, /invite |
| API key use | /api/* with Authorization headers |
Attacker goals:
- Guess the right password for an existing account
- Lock legitimate users out as a form of denial of service
- Use one account to test stolen passwords from other breaches (credential stuffing)
- Enumerate which usernames or emails exist on your system
Core Brute-Force Protection Concepts
Three Main Levers
You protect against brute force by combining these three ideas:
- Detection
Notice suspicious patterns: - too many failed attempts per IP
- too many failed attempts per user
- too many attempts across the whole system
- Limiting speed
Slow or block attackers: - rate limits
- temporary blocks
- longer delays after each failure
- Improving guesses’ effectiveness
Reduce value of each guess: - strong password rules and password managers
- 2FA so password alone is not enough
- single-use, short-lived tokens for sensitive actions
Do not rely on a single control. Robust protection comes from layers: rate limits, IP tracking, account tracking, lockouts, delays, and strong credential handling.
Simple Rate Limiting
What Is Rate Limiting?
Rate limiting puts a cap on how many requests are allowed in a period.
Examples:
- Max 10 login attempts per IP per minute
- Max 5 password reset requests per email per hour
If the limit is exceeded, you return a response such as:
429 Too Many Requests403 Forbiddenfor some sensitive cases
Rate limiting does not stop all brute force attempts, but it makes attacks slower and more expensive.
Fixed Window vs Sliding Window
Two common rate limit algorithms:
| Algorithm | How it works | Pros | Cons |
|---|---|---|---|
| Fixed window | Count requests in fixed intervals, such as each minute | Simple to implement | Bursts allowed at window edges |
| Sliding window | Count requests over a moving time window, such as last 60s | Smoother, fairer limiting | Slightly more complex |
For example, with a limit of 10 requests per minute:
- Fixed window: The count resets at
00:00:00,00:01:00, etc. - Sliding window: At any time, count all requests from the last 60 seconds.
Example: IP-Based Rate Limit
Imagine an endpoint /auth/login. You might implement:
- At most 10 login attempts per IP per minute
- At most 100 login attempts per IP per day
Pseudo-logic:
key = "login:ip:" + client_ip
current = GET(key) // from Redis
if current >= 10 in last 60 seconds:
return 429 Too Many Requests
INCR(key)
EXPIRE key in 60 secondsThis uses a cache such as Redis to track counts.
Why IP-based only is not enough
Attackers use:
- Botnets, many IPs
- Cloud providers, rotating IPs
- Proxies and VPNs
So you must also track per-account failures.
Account-Based Protections
Tracking Failed Logins Per Account
You should record failed login attempts per username or email, not just per IP.
Example rules:
- After 5 failed attempts for an account in 15 minutes, slow down further attempts.
- After 10 failed attempts, temporarily lock the account for 15 minutes.
Simplified data:
| Account (email) | Failures | Last failure | Status |
|---|---|---|---|
| alice@example.com | 0 | n/a | active |
| bob@example.com | 4 | 2026-08-27 10:01:12 | active |
| eve@example.com | 11 | 2026-08-27 09:59:48 | locked 15m |
You can store:
failed_login_countlast_failed_login_atlockout_untiltimestamp
Temporary Account Lockout
A temporary lockout makes brute-force attacks harder.
Example policy:
- 5 failed logins in 15 minutes
- Lock account for 15 minutes
Pseudo-logic:
if now < user.lockout_until:
return 423 Locked // or 429, or 403
if password_correct:
reset failed_login_count
user.lockout_until = null
log login success
return success
else:
increment failed_login_count
if failed_login_count >= 5:
user.lockout_until = now + 15 minutes
log login failure
return errorBalancing security and usability
Too aggressive lockouts can allow attackers to:
- Lock out many users intentionally as a denial-of-service attack
To reduce abuse:
- Combine lockouts with IP-based rate limits
- Use progressive delays before full lockout
- Notify users of suspicious lockouts by email and provide a recovery path
Progressive Delays and Backoff
Why Use Delays?
A simple lockout might be too strict. Instead, you can slow each additional attempt.
For example:
- 1st to 3rd failure: no delay
- 4th failure: 2 seconds delay
- 5th failure: 4 seconds delay
- 6th failure: 8 seconds delay
This is called progressive backoff.
Progressive delays can slow attackers without completely blocking legitimate users who mistype their password a few times.
Example Backoff Strategy
You can use an exponential formula:
$$
\text{delay\_seconds} = \min(2^{n-1}, 60)
$$
Where:
- $n$ is the number of consecutive failed attempts
- Delay is capped at 60 seconds
Table:
| Fail count (n) | Delay formula | Delay (seconds) |
|---|---|---|
| 1 | $2^{0}$ | 1 |
| 2 | $2^{1}$ | 2 |
| 3 | $2^{2}$ | 4 |
| 4 | $2^{3}$ | 8 |
| 5 | $2^{4}$ | 16 |
| 6 | $2^{5}$ | 32 |
| 7+ | min($2^{6}$, 60) | 60 |
Implementation idea:
- Track consecutive failures since last success.
- Before checking the password, compute the delay.
- Sleep for the delay.
- Then continue.
Note: Use server-side sleep carefully so you do not block worker threads. In async frameworks, use non-blocking sleep.
IP-Based and Network-Level Protections
IP Rate Limits
As mentioned earlier, IP-based limits are a first layer:
- Per IP per minute
- Per IP per hour
- Per IP per day
They protect against single hosts and against some abuse from common proxies.
IP Reputation and Blocking
Some systems integrate with IP reputation lists or maintain internal IP blacklists.
Possible actions:
- Block traffic from known malicious IPs.
- Challenge suspicious IPs with extra hurdles such as captchas.
- Use separate login thresholds for trusted vs unknown regions.
Country or ASN based rules
You might add stricter limits for:
- Countries where you do not have users
- Autonomous Systems (ASNs) used by large cloud providers
Be careful to avoid unfairly blocking legitimate users.
Network-Level Tools
Web servers, proxies, and firewalls often have built-in protections:
- Nginx
limit_reqfor rate limiting per IP - Cloudflare or other CDNs with security rules and bot protection
- WAF (Web Application Firewall) rules that detect login abuse patterns
It is a good idea to:
- Implement some protection at infrastructure level
- Implement some protection inside your application logic
This gives redundancy. If one layer fails, the other still protects you.
CAPTCHA and User Interaction Checks
When to Use a CAPTCHA
A CAPTCHA tries to separate humans from bots.
You might require a CAPTCHA:
- After several failed login attempts
- For the first few login attempts from an unknown device
- For mass signup or password reset requests
Approach:
- Do not add CAPTCHA to every single login, or usability will suffer.
- Trigger it only after detection of suspicious behavior.
Implementation Pattern
- User fails login 3 times.
- On next login attempt, the frontend displays a CAPTCHA challenge.
- Backend verifies the CAPTCHA token with the CAPTCHA provider.
- Backend checks password only if CAPTCHA is valid.
Example login flow:
POST /auth/login
Body: { "email": "...", "password": "...", "captcha_token": "..." }
1. Check if account is in "captcha required" mode.
2. If yes, verify captcha_token with provider.
3. If CAPTCHA fails, return error.
4. If CAPTCHA passes, proceed with normal login checks.Some systems also use invisible or risk-based CAPTCHAs that do not always require explicit user input.
Credential Stuffing and Password Reuse
What Is Credential Stuffing?
Attackers often use username/password pairs stolen from other websites and try them on your site.
Pattern:
- They already know
emailandpasswordcombo. - They test if the same password works on your service.
- Often it does, because many users reuse passwords.
Credential stuffing is related to brute-force attacks, but instead of random passwords, they use likely passwords, which increases success rate.
Defenses Against Credential Stuffing
Key strategies:
- Rate limiting
- Per account and IP, as before.
- Known password checks
- Check passwords against known breached password lists, for example "Have I Been Pwned" API, at password change or registration.
- Reject obviously compromised passwords.
- 2FA / MFA
- Even if password is correct, login still requires time-based code or hardware token.
- Suspicious login detection
- New device, unusual location, or impossible travel.
- Trigger additional verification steps, such as email code.
- Login alerts
- Notify users by email when login from a new country, device, or IP occurs.
Password reuse makes brute-force and credential stuffing far more dangerous. Encourage and support password managers and strong unique passwords.
Protecting One-Time Codes and Tokens
Brute-force is not only about passwords. Any short code or token can be guessed if not protected.
One-Time Passwords (OTP) and SMS/Email Codes
Typical codes:
- 4 to 8 digit numeric codes, for example
123456 - Short alphanumeric codes, for example
AB9X2F
If an attacker can try many codes, they will eventually guess correctly.
Example risk
- A 6-digit code has $10^6 = 1\,000\,000$ possibilities.
- If an attacker can try 1,000 codes per minute, they need at most 1,000 minutes in the worst case, but often less.
Best Practices for OTPs
- Short lifetime
- Codes expire quickly, for example 5 to 10 minutes.
- Use once
- Once a code is used successfully, it becomes invalid immediately.
- Attempt limits
- Limit number of tries, for example 3 to 5 attempts per code.
- Per user and per IP tracking
- Lock further attempts for some time if too many failures occur.
Example: Confirming 2FA Code
Flow:
- User enters code from their authenticator app.
- Backend checks:
- Is the account locked for 2FA attempts?
- Has there been too many recent failures?
- Does the code match the expected TOTP for this time?
- If correct:
- Mark login as fully authenticated.
- Reset 2FA failure counter.
- If incorrect:
- Increment 2FA failure counter.
- Possibly add delay or temporarily lock further attempts.
Handling Error Messages and Enumeration
Avoid User Enumeration
Brute-forcing credentials often begins with identifying valid accounts.
Do not reveal whether an account exists. For example:
- Bad:
- "No account found with that email"
- "User not found" on login form
- Better:
- "If the email and password are correct, you will receive a login"
- "Invalid email or password"
Same rule for:
- Password reset
- Account recovery
- Verification
Error messages should avoid confirming whether a username or email exists in your system.
Consistent Response Times
Attackers can also detect valid accounts by:
- Measuring response time differences
Example:
- If you return fast for non-existing users and slow for existing ones, attackers can probe emails to see which exist.
Aim for:
- Similar processing paths and timing for both cases.
Sometimes you can:
- Perform fake hashing or checks even when the account does not exist, to keep timing similar.
Logging, Monitoring, and Alerting
What to Log
To detect brute-force attacks, log at least:
- Failed login attempts
- Timestamp
- IP address
- Username or email tried
- User agent
- Successful login attempts
- Account lockouts and unlocks
- Blocked requests due to rate limit or WAF
Avoid logging passwords, codes, or full tokens.
Example Log Entry
{
"event": "login_failed",
"email": "alice@example.com",
"ip": "203.0.113.45",
"user_agent": "Mozilla/5.0 ...",
"reason": "invalid_password",
"failed_count": 4,
"lockout_until": null,
"timestamp": "2026-08-27T10:15:23Z"
}Monitoring and Alerts
You can configure alerts for:
- Sudden spike in total failed logins per minute
- Many failed logins from one IP or subnet
- Many different accounts attacked from one IP
- Many lockouts in a short period
Use tools covered elsewhere in logging and monitoring chapters, such as:
- Centralized log systems
- Metrics and dashboards
- Alerting systems (PagerDuty, email alerts, etc.)
Practical Design Guidelines
Combine Multiple Controls
A practical brute-force protection setup might include:
- Per IP rate limiting on login and reset endpoints
- Per account failure tracking and temporary lockouts
- Progressive delays after repeated failures
- CAPTCHA after several failures or for untrusted traffic
- Short-lived and single-use codes with attempt limits
- Consistent error messages regardless of account existence
- Two-factor authentication for higher-value accounts
- Monitoring and alerting based on logs and metrics
Example Policy for a Typical Web App
For a normal consumer application, you might choose:
- Login:
- Max 5 failed attempts per account per 15 minutes, then 15 minutes lockout
- Max 10 login attempts per IP per minute
- Add 1–16 seconds progressive delay after each failure starting from the 3rd failure
- CAPTCHA required after 5 failures within 1 hour
- Password reset:
- Max 3 reset emails per account per hour
- Max 10 reset emails per IP per hour
- Every reset token valid for 15 minutes and single use
- Max 5 incorrect reset token submissions per user per hour
- 2FA:
- Code valid for a short window, for example 30 seconds to 2 minutes
- Max 5 incorrect OTP submissions before temporary 2FA lockout
These numbers are just examples. In real systems, you must tune them based on your user base and risk level.
Trade-offs and Pitfalls
Denial of Service via Lockouts
If you lock accounts too aggressively:
- An attacker can intentionally try wrong passwords for many users and lock them out.
- That becomes a denial-of-service attack.
Mitigation strategies:
- Use shorter lockouts and more progressive delays instead of long lockouts.
- Use combinations of IP and account tracking.
- Notify users about suspicious activity and give them a way to recover.
Shared IPs and NAT
Many users may share one IP:
- Corporate networks
- Universities
- Mobile carriers
If your per IP limits are too strict, you might block many legitimate users at once.
Mitigation strategies:
- Use soft IP limits that slow down but do not completely block.
- Rely more on per account and device-based detection.
Bot Evasion
Advanced bots can:
- Solve some CAPTCHA types
- Rotate IPs
- Mimic user-agents and cookies
So you cannot rely on only one type of control. This is why the defense in depth idea is critical.
Summary
Brute-force protection is about making repeated guessing:
- Slow to perform
- Difficult to scale
- Easy to detect
Key elements:
- Per IP and per account rate limits
- Temporary lockouts and progressive delays
- Protection for one-time codes and tokens, not just passwords
- Consistent and non-revealing error messages
- Logging and monitoring to see and respond to attacks
- Additional layers like CAPTCHA and 2FA
By thoughtfully combining these techniques, you significantly reduce the risk that attackers can break into accounts through brute-force or credential stuffing attacks, while keeping your application usable for real users.
Views: 9
KAHIBARO