Segurança envolve trade-offs reais de usabilidade,
performance e custo de implementação. As decisões
abaixo forçam articular por que uma escolha é mais
segura que outra — e o que você está aceitando ao
escolher o caminho mais simples.
JWT stateless ou sessions com armazenamento central?
JWT stateless permite que qualquer instância do
serviço valide o token sem consultar banco — bom
para escala horizontal, zero latência de lookup.
O problema é revogação: um JWT válido por 1 hora
não pode ser invalidado antes de expirar sem
algum mecanismo centralizado (blocklist), o que
elimina a vantagem de ser stateless. Sessions
centralizadas (Redis) permitem revogação imediata
mas adicionam latência de lookup e dependência.
A solução prática usada na indústria: JWT com
tempo de expiração curto (15 minutos) + refresh
token de longa duração armazenado no servidor.
Isso limita a janela de risco sem precisar de
blocklist de tokens de acesso.
RBAC ou ABAC — quando a complexidade de ABAC se justifica?
RBAC se justifica quando as regras de acesso
são baseadas em papel e relativamente estáticas:
"admin pode fazer tudo, editor pode publicar,
reader só lê". Simples de implementar, auditar
e explicar. ABAC se justifica quando as regras
dependem de contexto dinâmico: "usuário pode
editar o documento se for o autor E o documento
estiver em rascunho E o horário for entre 8h e 18h".
RBAC com atributos extras (chamado às vezes de
RBAC+) frequentemente resolve 80% dos casos de ABAC
sem a complexidade total. ReBAC (Zanzibar) é o
caminho para sistemas multi-tenant onde as relações
entre entidades definem o acesso — Google Drive,
GitHub, Notion são exemplos.
Armazenar segredos em variáveis de ambiente, secrets manager ou Vault?
Variáveis de ambiente são melhores que hardcoded
no código, mas piores que todos percebem: acabam
em logs (quando o processo imprime o ambiente),
em dumps de crash, em docker inspect, e em
arquivos .env que frequentemente vão para o
repositório. Secrets Manager (AWS, GCP) é o
padrão mínimo para produção: segredos não ficam
no processo, rotação é mais fácil, acesso é
auditado. Vault adiciona dynamic secrets (o
segredo é gerado no momento da solicitação e
expira com a lease) — mais seguro, mais complexo.
A decisão prática: variáveis de ambiente para
desenvolvimento local, Secrets Manager para
produção inicial, Vault quando dynamic secrets
e auditoria avançada são requisito.
Certificate pinning: sim ou não?
Certificate pinning fixa o certificado ou a
chave pública que o cliente aceita — protege
contra man-in-the-middle mesmo com CA comprometida.
O problema é operacional: quando o certificado
expira ou é rotacionado, o cliente para de
funcionar até ser atualizado. Em apps mobile
isso pode ser catastrófico — usuários com versões
antigas perdem acesso. A recomendação atual da
indústria é pinning de CA raiz (não do certificado
leaf) e backup pins — duas chaves aceitas ao mesmo
tempo durante a transição. Para APIs consumidas
por clientes que você controla e pode atualizar
rapidamente, pinning faz sentido. Para APIs
públicas com clientes de terceiro, os riscos
operacionais raramente justificam.
Validação de input: denylist (bloquear o ruim) ou allowlist (permitir só o bom)?
Denylist bloqueia padrões conhecidos de ataque
(DROP TABLE, <script>, ../../../). O problema
é que atacantes são criativos: encodings diferentes
(URL encoding, unicode, double encoding), casos
extremos, e combinações novas passam pelo filtro.
Denylist é um jogo de whack-a-mole sem fim.
Allowlist define o que é válido e rejeita tudo
o mais: um campo de nome aceita apenas letras,
espaços e hífens — qualquer outra coisa é inválida.
Mais restritivo, mas muito mais seguro. A regra:
sempre preferir allowlist (também chamada de
positive validation). Denylist apenas como camada
adicional em situações onde allowlist é impossível
(campos de texto livre irrestrito).