security: rate limit no login, cookies Secure condicional, security headers e deps com CVE

- 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>
This commit is contained in:
Gustavo
2026-08-15 11:17:41 -03:00
co-authored by Claude Sonnet 5
parent b0830651f4
commit d7a08ddd8d
7 changed files with 232 additions and 7 deletions
+7
View File
@@ -3,5 +3,12 @@ WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app
# Não roda como root dentro do container — reduz o estrago de uma
# eventual RCE (mesmo sem vulnerabilidade conhecida hoje, é uma
# mitigação de baixo custo e sem efeito colateral aqui, já que a app não
# precisa gravar nada no filesystem além de escutar a porta 8000, que não
# é privilegiada).
RUN useradd --no-create-home --shell /usr/sbin/nologin painel
USER painel
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]