## 28) Login failure delay can enable app-layer DoS
This commit is contained in:
@@ -22,7 +22,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
|
||||
import structlog
|
||||
from fastapi import APIRouter, Request, Response, status
|
||||
from fastapi import APIRouter, Request, Response
|
||||
|
||||
from app.dependencies import (
|
||||
AuthDep,
|
||||
@@ -98,11 +98,16 @@ async def login(
|
||||
session_repo=session_ctx.session_repo,
|
||||
)
|
||||
except ValueError as exc:
|
||||
# Add delay on wrong password to slow down brute-force attacks.
|
||||
# The bcrypt checkpw already takes ~100ms at cost factor 12,
|
||||
# but an extra 10 seconds makes automation much less feasible.
|
||||
await asyncio.sleep(10.0)
|
||||
log.warning("login_failed", client_ip=client_ip, error=str(exc))
|
||||
# Progressive penalty delay on wrong password to slow down brute-force
|
||||
# attacks without exhausting request capacity (app-layer DoS resistance).
|
||||
penalty = rate_limiter.record_failure(client_ip)
|
||||
acquired = rate_limiter.acquire(client_ip)
|
||||
try:
|
||||
if acquired:
|
||||
await asyncio.sleep(penalty)
|
||||
finally:
|
||||
rate_limiter.release(client_ip)
|
||||
log.warning("login_failed", client_ip=client_ip, error=str(exc), penalty=penalty)
|
||||
raise AuthenticationError(str(exc)) from exc
|
||||
|
||||
response.set_cookie(
|
||||
|
||||
Reference in New Issue
Block a user