Skip to content
  • Gabriel Antico's avatar
    4315f60c
    feat(auth): per-email lockout dopo 5 fallimenti consecutivi · 4315f60c
    Gabriel Antico authored
    In topologia NAT corporate il rate-limit per-IP non discrimina utenti
    reali (tutti CoinSafe escono da 192.168.0.1 verso Caddy). HDRCR-117 ha
    alzato il limite a 60/min per non bloccare utenti legittimi, ma serviva
    una difesa per brute-force su una singola credenziale che fosse
    indipendente dall'IP.
    
    `app/auth/lockout.py` implementa counter+flag su redis con TTL:
    - `auth:failed:<email>` counter di fallimenti consecutivi (TTL 5 min)
    - `auth:locked:<email>` flag di lockout attivo (TTL 15 min)
    
    5 fallimenti su `alice@x` dentro 5 min lockano `alice@x` per 15 min, da
    qualunque IP. Login riuscito resetta il counter. L'errore restituito è
    sempre lo stesso 401 generico per evitare user-enumeration.
    
    Se redis è giù degrada silenziosamente: niente lockout, ma il
    rate-limit per-IP del Limiter resta come backstop.
    
    Test fakeano `redis.asyncio.from_url` con uno store in-memory che
    implementa solo i comandi usati — niente redis richiesto in CI.
    
    HDRCR-118 #comment Aggiunto app/auth/lockout.py e integrato nel /auth/login
    4315f60c
    feat(auth): per-email lockout dopo 5 fallimenti consecutivi
    Gabriel Antico authored
    In topologia NAT corporate il rate-limit per-IP non discrimina utenti
    reali (tutti CoinSafe escono da 192.168.0.1 verso Caddy). HDRCR-117 ha
    alzato il limite a 60/min per non bloccare utenti legittimi, ma serviva
    una difesa per brute-force su una singola credenziale che fosse
    indipendente dall'IP.
    
    `app/auth/lockout.py` implementa counter+flag su redis con TTL:
    - `auth:failed:<email>` counter di fallimenti consecutivi (TTL 5 min)
    - `auth:locked:<email>` flag di lockout attivo (TTL 15 min)
    
    5 fallimenti su `alice@x` dentro 5 min lockano `alice@x` per 15 min, da
    qualunque IP. Login riuscito resetta il counter. L'errore restituito è
    sempre lo stesso 401 generico per evitare user-enumeration.
    
    Se redis è giù degrada silenziosamente: niente lockout, ma il
    rate-limit per-IP del Limiter resta come backstop.
    
    Test fakeano `redis.asyncio.from_url` con uno store in-memory che
    implementa solo i comandi usati — niente redis richiesto in CI.
    
    HDRCR-118 #comment Aggiunto app/auth/lockout.py e integrato nel /auth/login
Loading