""" Proteção CSRF via "double-submit cookie": o middleware registrado em app/main.py garante que todo navegador que acessa o painel tenha um cookie "csrf_token" (gerado uma vez, aleatório, HttpOnly). Todo formulário que muda estado (POST) embute esse mesmo valor como campo oculto — os templates leem de request.state.csrf_token, que o middleware preenche a cada requisição. Toda rota POST que muda estado usa Depends(exigir_csrf), que confere se o valor enviado no formulário bate com o valor do cookie. Por quê isso barra CSRF: um site hostil consegue forçar o navegador da vítima a mandar um POST para o painel (o cookie vai junto automaticamente), mas não consegue LER o valor do cookie para replicá-lo como campo oculto do formulário forjado — só JavaScript rodando na própria origem do painel consegue (e não roda em outra origem, por causa do same-origin policy). """ import secrets from fastapi import HTTPException, Request, status CSRF_COOKIE_NAME = "csrf_token" def gerar_token() -> str: return secrets.token_urlsafe(32) def token_do_contexto(request: Request) -> str: """ Lê o token que o middleware colocou em request.state.csrf_token. Retorna "" se o middleware não estiver montado (por exemplo, testes que sobem só um router isolado, sem app/main.py) — nesse caso o formulário simplesmente não terá csrf_token preenchido, e exigir_csrf abaixo vai rejeitar o POST a menos que o teste monte o cookie/campo manualmente. """ return getattr(request.state, "csrf_token", "") async def exigir_csrf(request: Request) -> None: """Dependency para toda rota POST que muda estado.""" form = await request.form() token_formulario = str(form.get(CSRF_COOKIE_NAME, "")) token_cookie = request.cookies.get(CSRF_COOKIE_NAME, "") if not token_cookie or not token_formulario or not secrets.compare_digest(token_formulario, token_cookie): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Token CSRF ausente ou inválido.")