- app/rate_limit.py: bloqueia por IP após 5 tentativas de senha erradas
em 5 minutos em POST /login (rota pré-autenticação, hoje sem nenhuma
proteção contra força bruta).
- Cookie de sessão (routers/auth.py) e cookie CSRF (main.py) passam a
sair com Secure quando a requisição chega em HTTPS (request.url.scheme)
— hoje é sempre False porque produção ainda serve HTTP puro, mas o
cookie passa a ficar protegido automaticamente assim que um TLS for
colocado na frente, sem precisar editar isso de novo.
- main.py: novo middleware de security headers (X-Content-Type-Options,
X-Frame-Options: DENY, Referrer-Policy, Content-Security-Policy,
Strict-Transport-Security condicional a HTTPS).
- requirements.txt: fastapi 0.115.0 -> 0.115.6 (starlette >=0.40.0,
corrige DoS por memória não limitada ao parsear multipart/form-data —
GHSA-f96h-pmfr-66vw), python-multipart 0.0.9 -> 0.0.20 (várias
vulnerabilidades de DoS corrigidas), jinja2 3.1.4 -> 3.1.6 (escapes de
sandbox). httpx/uvicorn/PyMySQL já estavam em versões sem CVE
conhecida — conferido via osv.dev.
- Dockerfile: container não roda mais como root.
Suíte completa (181 testes) passando, incluindo testes novos para o
rate limiter (unitários + integração via POST /login) e smoke test
manual confirmando headers, fluxo de login com CSRF e o bloqueio por
força bruta.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
_proximo_seguro() (app/routers/auth.py) so recusava next que comecasse
com "//", mas navegadores modernos tratam "/\" como equivalente a
"//" (URL protocol-relative), entao next=/\evil.com ainda era um open
redirect explorável via link de login malicioso.
Bloqueia tambem o prefixo "/\".
Teste de regressao em tests/test_router_auth.py: POST /login com
next=/\evil.com precisa redirecionar para /clientes, nao para o host
externo.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>