C.W.K.
Stream
Lesson 03 of 05 · published

Beyond Hard Lockout — Backoff, Jitter, Honeypots

~15 min · backoff, honeypot, jitter

Level 0Greenhorn
0 XP0/53 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

Hard lockout-after-N is the simplest defense. Three more techniques are worth knowing for when the threat model warrants more sophistication.

1. Exponential backoff — slow down, don't lock out

Instead of "5 strikes, you're out", make each successive failure cost more time. The user barely notices; the attacker hits a wall.

2. Jitter — defeat timing side channels

The 0.5-second constant-time delay (Track 5) prevents response time from leaking info. Adding random jitter (e.g. 0.4–0.6s instead of exactly 0.5s) further muddies any statistical attack on timing.

3. Honeypot endpoints — spot reconnaissance

Add a fake endpoint like /wp-admin or /.env that always returns 404 but logs a "scanner detected" event and (optionally) blacklists the source IP.

What NOT to do

  • Don't return different errors for "wrong PIN" vs "no such user" — it leaks user enumeration. Always the same response.
  • Don't lock out on the username only in multi-user systems — attackers can DOS by trying many wrong PINs for one user. Per-IP lockout is correct.
  • Don't show the attempt counter publicly ("3 attempts remaining") — that's free intel for the attacker. Show only "Invalid PIN".

Code

Exponential backoff·python
# delay = 2 ** (fail_count - 1) seconds
# fail 1: 1s, fail 2: 2s, fail 3: 4s, fail 4: 8s, fail 5: 16s, ...
delay = min(60, 2 ** (attempt_count(ip)))
await asyncio.sleep(delay)
Jitter·python
import random
await asyncio.sleep(0.4 + random.random() * 0.2)   # 0.4 – 0.6 sec
Honeypot middleware — auto-blacklist scanners·python
HONEYPOTS = ("/wp-admin", "/wp-login.php", "/.env", "/.git/config",
             "/admin.php", "/phpmyadmin")

@app.middleware("http")
async def honeypot(request, call_next):
    if request.url.path in HONEYPOTS:
        ip = client_ip(request)
        log_scanner_hit(ip, request.url.path)
        blacklist(ip, "honeypot_" + request.url.path)
        return Response("Not Found", status_code=404)
    return await call_next(request)

External links

Exercise

Add the honeypot middleware to your app. From a different machine (or just curl), hit /wp-admin. Verify: (1) the response is 404, (2) the source IP appears in security_blacklist with reason starting honeypot_, (3) subsequent requests from that IP get 403 from your middleware. You've now got proactive scanner blocking.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.