Segurança de APIs Python com OWASP
Uma API Python segura não nasce de um middleware isolado. A resposta rápida é: autentique quem chama, autorize cada ação e cada objeto, valide toda entrada, exponha somente origens necessárias, limite abuso, mantenha secrets fora do código e registre eventos sem registrar credenciais. Esses controles precisam existir no desenho, no código, na infraestrutura e nos testes.
A OWASP API Security organiza riscos recorrentes, mas não é uma lista para marcar depois do deploy. Use-a durante a modelagem de ameaças: quais dados existem, quem pode lê-los, como um atacante automatizaria chamadas e qual dano ocorreria se uma credencial vazasse? A REST Security Cheat Sheet da OWASP complementa o trabalho com recomendações operacionais.
Autenticação não é autorização
Autenticação (authn) confirma a identidade. Autorização (authz) decide se essa identidade pode executar a operação sobre aquele recurso. Um token válido não autoriza automaticamente GET /invoices/42. O servidor deve verificar se a fatura pertence ao usuário ou se seu papel concede acesso. Essa checagem evita a autorização quebrada por objeto, um risco central em APIs.
Prefira protocolos estabelecidos, tokens curtos e escopos pequenos. Valide assinatura, emissor, público, expiração e algoritmo no servidor; não aceite o algoritmo indicado pelo cliente sem uma configuração fechada. Para sessões, cookies Secure, HttpOnly e SameSite reduzem exposição no navegador. A documentação de segurança do FastAPI mostra os componentes OAuth2 disponíveis, mas a política de acesso continua sendo responsabilidade da aplicação.
O exemplo mantém a regra perto do recurso:
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from pydantic import BaseModel, ConfigDict
app = FastAPI()
class User(BaseModel):
id: int
is_admin: bool = False
class Invoice(BaseModel):
model_config = ConfigDict(extra="forbid")
id: int
owner_id: int
total_cents: int
def current_user() -> User:
# Em produção, valide aqui uma credencial real e suas claims.
return User(id=7)
def load_invoice(invoice_id: int) -> Invoice | None:
return Invoice(id=invoice_id, owner_id=7, total_cents=5900)
@app.get("/invoices/{invoice_id}")
def get_invoice(invoice_id: int, user: Annotated[User, Depends(current_user)]):
invoice = load_invoice(invoice_id)
if invoice is None:
raise HTTPException(status_code=404, detail="Invoice not found")
if invoice.owner_id != user.id and not user.is_admin:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
return invoice
Não confie em owner_id enviado no corpo para determinar o proprietário; derive-o da identidade autenticada. Para uma introdução ao framework, consulte o tutorial de FastAPI para APIs RESTful.
Validação e respostas previsíveis
Valide tipo, tamanho, formato, faixa e semântica. Rejeitar campos extras reduz mass assignment: o cliente não pode acrescentar is_admin=true a um modelo permissivo. Limite também tamanho do corpo, quantidade de itens e profundidade de estruturas no proxy ou servidor. Serializers ajudam a definir esse contrato, como mostra o guia de Django REST Framework, mas consultas SQL ainda devem ser parametrizadas e caminhos de arquivo precisam de controles próprios.
Não devolva tracebacks, SQL, caminhos internos ou detalhes criptográficos. Respostas públicas podem ter uma mensagem estável e um request_id; o diagnóstico completo fica em um canal interno protegido. O guia de tratamento de erros em Python ajuda a evitar except Exception que silencia falhas importantes.
CORS e rate limiting têm funções específicas
CORS diz ao navegador quais origens podem ler respostas. Não substitui authn, authz ou proteção CSRF. Liste origens exatas, métodos e cabeçalhos necessários. Evite refletir qualquer Origin; credenciais com origem curinga são especialmente perigosas. Separe configurações de desenvolvimento e produção.
Rate limiting reduz força bruta, raspagem e consumo acidental, mas não corrige endpoints caros. Aplique cotas por identidade e, antes da autenticação, por IP ou chave de cliente. Login e recuperação de senha merecem limites mais estritos. Em múltiplas instâncias, use um contador compartilhado e respostas 429 com Retry-After. Combine isso com timeouts, paginação, limites de upload e orçamento de concorrência. Escolha números após observar tráfego legítimo; um valor copiado pode bloquear clientes ou não conter abuso.
Secrets, dependências e configuração
Credenciais não pertencem ao Git, arquivo .env versionado, log, traceback ou imagem de container. Em produção, injete-as por um gerenciador de secrets, dê acesso mínimo à identidade da carga e planeje rotação. Falhe ao iniciar quando uma configuração obrigatória estiver ausente. Se uma chave vazar, revogue-a; apagar o commit não elimina cópias.
Fixe e revise dependências, mantenha Python e bibliotecas suportados e execute análise no pipeline. Ambientes isolados, descritos no guia de venv em Python, evitam conflitos, mas não substituem atualização e inventário. Containers devem rodar sem root e com somente os arquivos necessários; veja as práticas de Docker com Python.
Logging útil sem dados sensíveis
Registre eventos estruturados: horário, rota normalizada, status, latência, identificador de requisição e identificador interno pseudônimo. Nunca registre corpo inteiro por padrão, Authorization, cookies, senha ou token. Mascaramento deve acontecer antes de o evento deixar o processo. Defina acesso, retenção e alertas para falhas repetidas de login, picos de 403, 429 e alterações administrativas.
Um request_id gerado pelo servidor permite correlacionar proxy, aplicação e banco sem expor dados pessoais. Não aceite cegamente um identificador externo: valide formato e tamanho ou gere outro. Para configurar níveis e handlers sem duplicação, consulte logging em Python.
Erros frequentes
- Verificar o token e esquecer a permissão sobre cada objeto.
- Usar CORS
*como suposta proteção da API. - Aceitar campos desconhecidos ou confiar em preço, papel e proprietário enviados pelo cliente.
- Aplicar um único limite global, fácil de contornar e ruim para clientes legítimos.
- Registrar headers e corpos completos para facilitar depuração.
- Manter chaves sem rotação ou compartilhar uma credencial entre serviços.
- Retornar erros diferentes que revelam se um e-mail ou recurso existe.
Checklist antes de publicar
- Modele recursos, papéis, escopos e casos de negação; teste acesso horizontal e vertical.
- Valide assinatura e claims, expire credenciais e tenha revogação ou rotação.
- Rejeite campos extras, limite payloads e parametrize consultas.
- Configure CORS com origens exatas e trate CSRF quando cookies autenticam.
- Aplique limites distribuídos, timeouts, paginação e limites de upload.
- Injete secrets fora do artefato e restrinja acesso por serviço.
- Remova dados sensíveis dos logs e proteja sua retenção.
- Automatize testes, incluindo
401,403,404,422e429, com pytest.
Segurança é uma propriedade contínua. Revise ameaças quando surgirem endpoints, integrações ou novos tipos de dado, acompanhe alertas e transforme incidentes e testes em controles repetíveis.