← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Brute-Force Protection

security CWE-307 OWASP A7:2021 Intermediate

Also Known As

rate limiting login account lockout credential stuffing defence login throttling

TL;DR

Defences against automated credential-guessing attacks — rate limiting login attempts, account lockout, CAPTCHA, and multi-factor authentication to make guessing passwords computationally infeasible.

Explanation

Brute-force attacks submit large volumes of credential combinations to authentication endpoints until one succeeds. Defence layers: (1) Rate limiting — restrict attempts per IP and per account (e.g. 5 failures per 10 minutes) using a token-bucket or sliding-window counter stored in Redis or APCu. (2) Progressive delays — exponential back-off after each failure slows automated tools without locking legitimate users out permanently. (3) Account lockout — temporarily disable an account after N failures; prefer time-based lockout over permanent to avoid denial-of-service. (4) CAPTCHA — challenges that are easy for humans and hard for bots, triggered after a threshold of failures. (5) MFA — a second factor renders the guessed password alone useless. In PHP, rate limiting is typically implemented in middleware or a firewall layer before the authentication logic. Credential stuffing (using breached credential databases) is a variant — defeated by checking submitted passwords against known-breached lists (HaveIBeenPwned API). Timing-safe comparison (hash_equals) prevents timing-based oracle attacks that can leak valid usernames.

Watch Out

Lockout policies must account for distributed attacks — if an attacker knows a username they can intentionally trigger lockouts to deny service to legitimate users. Consider CAPTCHA escalation over hard lockout.

Common Misconception

IP-based rate limiting alone is not sufficient — distributed botnets rotate IPs, so per-account attempt counting is essential alongside per-IP limits.

Why It Matters

Authentication endpoints are among the most frequently targeted surfaces on the web — without brute-force protection, weak or reused passwords are cracked in minutes by automated tools.

Common Mistakes

  • Rate limiting only by IP — attackers distribute requests across thousands of IPs; always also count per-username.
  • Permanent account lockout — creates a denial-of-service vector where an attacker locks every account by deliberate failures.
  • Not applying rate limits to password-reset endpoints — reset flows are equally brute-forceable and often less protected.
  • Leaking whether a username exists via different error messages — use a generic 'invalid credentials' message regardless of which field is wrong.

Code Examples

💡 Note
The bad example allows unlimited attempts. The fix tracks failures per hashed email in Redis with a 15-minute TTL — the counter resets on success and the same generic exception message is used for both 'user not found' and 'wrong password' to prevent username enumeration.
✗ Vulnerable
// No rate limiting — unlimited attempts:
public function login(string $email, string $password): bool {
    $user = User::findByEmail($email);
    return $user && password_verify($password, $user->password_hash);
}
✓ Fixed
// Redis-backed per-account rate limit:
public function login(string $email, string $password): bool {
    $key = 'login_attempts:' . hash('sha256', $email);
    $attempts = (int) $this->redis->get($key);
    if ($attempts >= 5) {
        throw new TooManyAttemptsException('Account temporarily locked');
    }
    $user = User::findByEmail($email);
    if (!$user || !password_verify($password, $user->password_hash)) {
        $this->redis->incr($key);
        $this->redis->expire($key, 900); // 15 min window
        throw new InvalidCredentialsException('Invalid credentials');
    }
    $this->redis->del($key); // reset on success
    return true;
}

Added 10 Apr 2026
Views 22
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Perplexity 3 SEMrush 3 Google 2 ChatGPT 1 Unknown AI 1 Ahrefs 1
crawler 10 crawler_json 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Add a Redis-backed attempt counter keyed by username; block for 15 minutes after 5 failures; apply the same limit to password-reset endpoints
🔗 Prerequisites
🔍 Detection Hints
login or authenticate method with no rate-limit check before password_verify()
Auto-detectable: ✓ Yes semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: High Context: Function Tests: Update
CWE-307 CWE-799 CWE-308

✓ schema.org compliant