Brute Force Attack
debt(d7/e5/b5/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints specify semgrep with automated:no, and the code_pattern is 'Login handler with no failed-attempt counter or lockout mechanism' — this is an absence-of-control pattern that requires deliberate manual inspection or a custom semgrep rule to catch; it won't surface from a default linter scan.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix requires adding rate limiting, account lockout, CAPTCHA, and switching to bcrypt/Argon2 — this touches the login handler, password reset endpoint, OTP endpoints, and the password hashing layer, spanning multiple files and components rather than a single-line patch.
Closest to 'persistent productivity tax' (b5). The applies_to contexts are web and api, meaning every authentication-related endpoint carries this concern. Tags include rate-limiting and owasp-top10, indicating it shapes how login, reset, and OTP flows must be designed and reviewed going forward, but it doesn't define the entire system shape.
Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7). The misconception field states explicitly: 'Locking an account after 5 attempts fully prevents brute force.' This is a well-known, widely-deployed defence that developers confidently apply yet it introduces denial-of-service risk and is bypassed by distributed low-and-slow attacks — a serious trap because the 'obvious' countermeasure is both incomplete and harmful.
Also Known As
TL;DR
Explanation
A brute force attack tries all possible combinations of characters until the correct password, token, or encryption key is found. Against a fast hash like MD5, an attacker can test billions of guesses per second on commodity hardware. Defences include: slow hashing algorithms (bcrypt, Argon2), account lockout or progressive delays after failed attempts, CAPTCHA for login forms, and multi-factor authentication. Rate limiting at the application and infrastructure level adds another layer.
Diagram
flowchart TD
ATK[Attacker tries passwords] --> RATE{Rate limit check}
RATE -->|under limit| TRY[Attempt login]
RATE -->|over limit| LOCK[429 Too Many Requests<br/>Retry-After header]
TRY -->|failed| COUNT[Increment failure counter]
COUNT --> THRESH{Threshold reached?}
THRESH -->|yes| LOCKOUT[Account lockout<br/>5 min exponential backoff]
THRESH -->|no| RATE
subgraph Defences
BCRYPT2[bcrypt Argon2 - slow hashing<br/>1000 attempts/s not billions]
NOTIFY2[Email user on lockout]
GEO[Block unusual geography]
end
style LOCK fill:#f85149,color:#fff
style LOCKOUT fill:#f85149,color:#fff
style BCRYPT2 fill:#238636,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- No rate limiting or account lockout on login, password reset, or OTP endpoints.
- Lockout based on username only — attackers distribute attempts across many accounts to avoid per-account limits.
- Using weak password hashing (MD5, SHA1) that makes offline cracking trivial after a database breach.
- CAPTCHA as the only defence — solvable by third-party services; rate limiting is also needed.
Code Examples
// No rate limiting — unlimited password guesses:
if ($_POST['password'] === $user['password_hash']) {
// login success
}
// Rate limit login attempts per IP and per account
class LoginController {
public function login(Request $req): Response {
$key = 'login_attempts:' . $req->ip() . ':' . $req->input('email');
if ($this->cache->get($key, 0) >= 5) {
return response()->json(['error' => 'Too many attempts'], 429);
}
if (!$this->auth->attempt($req->only('email', 'password'))) {
$this->cache->increment($key);
$this->cache->expire($key, 900); // 15-minute window
return response()->json(['error' => 'Invalid credentials'], 401);
}
$this->cache->delete($key);
return response()->json(['token' => $this->auth->token()]);
}
}