Pular para o conteúdo
Categoria: OPSEC11 min de leitura

Threat Modeling com STRIDE em Sprints: Exemplo Completo de Microservico

Por Lucas Andrade ·

Como aplicar STRIDE em um microservico real de pagamentos dentro de uma sprint de duas semanas, com diagrama, ameacas priorizadas e mitigacoes acionaveis.

Threat Modeling com STRIDE em Sprints: Exemplo Completo de Microservico

O threat modeling morre numa gaveta quando vira uma reuniao de quatro horas sem owner e um PDF de 40 paginas que ninguem reabre. Na Basilisk OffSec cabeamos STRIDE em sprints de duas semanas usando um microservico de pagamentos como cobaia: um dev de backend, um SRE, um pesquisador ofensivo, 90 minutos no kickoff e 30 minutos de revisao no meio do sprint. A saida nao e um documento, sao 12 issues de Jira com mitigacoes verificaveis. Este post mostra exatamente como rodamos no servico payments-api, que recebe webhooks do PSP e fala com Postgres, Redis e um KMS, e como cada letra do STRIDE virou um patch real no codigo dentro do mesmo sprint em vez de um achado que envelhece numa wiki.

Por que o threat modeling morre numa gaveta

O modo de falha e previsivel: um workshop big-bang produz um documento exaustivo, o documento nunca vira trabalho, e seis semanas depois a arquitetura ja derivou para alem dele. A solucao e tornar o threat modeling pequeno, recorrente e atado a saida. Limitamos o kickoff a 90 minutos, restringimos o escopo a um servico e suas fronteiras de confianca imediatas, e exigimos que cada ameaca saia da sala como issue rastreado com criterio de aceitacao, nao como um paragrafo. O pesquisador ofensivo mantem a sessao honesta perguntando "como eu exploraria isso de verdade" em vez de "isso e teoricamente ruim". O time-boxing forca priorizacao: voce modela primeiro os fluxos que cruzam uma fronteira de confianca, porque e ali que operam os atacantes reais, e aceita que uma sessao mais curta a cada sprint ganha de uma heroica que acontece uma vez e apodrece.

O DFD e as fronteiras de confianca

Antes de modelar, desenhamos o Data Flow Diagram no draw.io com quatro tipos de elemento: entidades externas (o PSP, o frontend), processos (payments-api, worker-reconciliation), datastores (Postgres tx_db, Redis idempotency_cache) e os fluxos entre eles. Marcamos as fronteiras de confianca explicitamente: internet, depois Cloudflare, depois ingress, depois o mesh interno, depois o KMS. O diagrama nao precisa ser bonito, precisa ser correto, e em 25 minutos todos assinaram embaixo. Quem ja construiu um lab sabe que um diagrama errado leva a testes errados, como cobrimos em Pentest Web do Zero: um Lab Seguro com DVWA, Juice Shop e Burp Suite. Mesma regra aqui: se o seu fluxo de webhook nao mostra a validacao HMAC antes do parse do JSON, voce esta modelando o servico que vive na sua cabeca, nao o que esta em producao.

Spoofing: provar quem realmente esta chamando

O spoofing apareceu primeiro no fluxo PSP para payments-api. O webhook chegava com um header X-Signature, mas a verificacao rodava depois de json.loads(body), deixando uma janela de parser-differential onde um evento forjado podia ser parcialmente processado antes de a verificacao de assinatura falhar. O fix foi validar o HMAC-SHA256 com uma chave rotacionada pelo KMS antes de tocar o body, usando comparacao de tempo constante via hmac.compare_digest para evitar um oraculo de timing. Tambem fixamos o algoritmo de assinatura aceito em vez de confiar num header fornecido pelo cliente, fechando um downgrade por confusao de algoritmo. A licao geral: autentique a mensagem antes de parsea-la, e nunca deixe a identidade ser afirmada pelo mesmo input nao confiavel em que voce esta prestes a confiar. Cada entidade externa do DFD recebeu a mesma pergunta, e foi ali que os achados de spoofing se agruparam.

Tampering: integridade de dados em repouso e em transito

O tampering apareceu no Redis: o cache de idempotencia nao tinha TTL fixo nem assinatura, entao um atacante com acesso a rede interna podia plantar entradas e disparar replays de cobranca ou suprimir os legitimos. Adicionamos um prefixo de key com namespace mais um HMAC curto sobre a key, e forcamos uma ACL com requirepass e TLS no Redis 7 para que o datastore deixe de ser uma zona de confianca mole so por estar dentro do mesh. No fio, exigimos mutual TLS entre a API e o worker para que um sidecar comprometido nao pudesse reescrever em silencio mensagens de reconciliacao. A pergunta de modelagem para cada fluxo e datastore foi direta: se um atacante sentasse aqui, o que ele poderia mudar e nos perceberiamos. Onde a resposta foi "mudar em silencio", adicionamos protecao de integridade, e onde foi "nao perceberiamos", o logging do qual a proxima letra depende.

Repudiation: tornar as acoes provaveis

O repudiation foi tratado com uma tabela audit_log append-only usando hashes encadeados, de modo que cada registro se compromete com o anterior e uma exclusao ou edicao silenciosa quebra a cadeia. E o mesmo padrao tamper-evident que usamos ao documentar pivoting atraves de redes segmentadas em Pivoting com Chisel e Ligolo-ng: Redes Segmentadas num Lab de Pentest, aplicado aqui a movimentacao de dinheiro em vez de acoes de operador. Logamos o principal autenticado, o id de request, o hash de estado antes-e-depois e um timestamp monotono para cada cobranca, estorno e decisao de reconciliacao. O ponto nao e coletar logs por coletar, e poder provar, apos um incidente, exatamente quem fez o que e em que ordem, sem depender de uma tabela mutavel que um atacante com acesso ao banco pudesse reescrever. Controles de repudiation sao baratos de adicionar na frente e quase impossiveis de reconstruir depois.

Information Disclosure: a categoria mais pesada

O Information Disclosure foi a categoria mais pesada com oito achados. Stack traces vazavam via 500s do FastAPI num ambiente de staging espelhado para prod, segredos surgiam numa rota /debug atras de um header magico que um estagiario de 2024 esqueceu de remover, e o endpoint /metrics do Prometheus expunha labels carregando card_bin. Corrigimos com middleware que so serializa {error_id, code} para o cliente, matamos a rota debug, e aplicamos um relabel_config no Prometheus para dropar labels sensiveis no momento do scrape. Para tornar o impacto concreto para o time demonstramos um POC equivalente a um SSRF puxando metadata da nuvem, um exercicio documentado em SSRF Desmistificado: Explorando Metadata da Nuvem num Lab AWS Local. Ver um token real puxado de um endpoint de metadata mudou a sala de "esse log esta ok" para "redigir tudo na fronteira".

Denial of Service: alem do rate limiting

O Denial of Service nao foi tratado como so rate limiting. Mapeamos amplificacao algoritmica: um endpoint /search aceitava um regex fornecido pelo cliente e batia no Postgres com LIKE %term%, que e ao mesmo tempo um risco de ReDoS e de full-scan. Substituimos por tsvector mais indice GIN e um cap de 64 caracteres no termo, transformando uma query ilimitada numa limitada. Adicionamos um token bucket por API-key no Envoy a 100 rps com burst de 200, e um circuit breaker no cliente KMS com pybreaker, porque o KMS gerenciado tem uma cota de 1200 ops por segundo por chave e ja tinhamos causado um incidente autoinfligido de 14 minutos em janeiro. A modelagem de DoS pergunta onde um input pequeno produz trabalho desproporcional, e cada ponto desses recebeu um limite, um cache ou um breaker para que um unico chamador nao consiga derrubar o servico.

Elevation of Privilege: fechar a lista

O Elevation of Privilege fechou a lista. O JWT interno da API usava HS256 com um unico segredo compartilhado entre seis servicos, o que significa que um unico servico comprometido pode cunhar tokens aceitos por todos os outros. Migramos para RS256 com chaves por servico guardadas no KMS, claims especificos de audiencia para que um token cunhado para um servico seja rejeitado por outro, e validacao completa de exp, nbf e iss num unico middleware entregue como a biblioteca interna basilisk-authz==2.3.0. Centralizar a verificacao numa biblioteca auditada removeu a deriva onde cada servico validava um pouco diferente, que e em si um caminho de elevacao. A pergunta de modelagem foi simples: se este componente esta totalmente dominado, o que o atacante ganha em outro lugar, e a resposta do segredo compartilhado foi "tudo", entao virou o fix de maior prioridade do lote.

Transformar ameacas em issues rastreados

No fim do sprint cada item virou um issue intitulado STRIDE-<letra>-<num>: <ameaca> com uma tag mitigation:<status>, porque uma ameaca sem ticket e uma ameaca que nao vai ser corrigida. Das 12 ameacas levantadas, 9 sairam como PRs mergeados dentro do mesmo sprint, 2 foram aceitas como risco residual com data de revisao documentada em 90 dias, e 1 virou um epic para refatorar o modulo de webhook. O custo total foi cerca de 4 horas de reunioes distribuidas mais o trabalho de codigo que ja estava no board do sprint. A disciplina que sustenta isso e recusar fechar a sessao de modelagem ate que cada ameaca levantada tenha owner, status e criterio de aceitacao verificavel, para que a saida seja um backlog que voce pode queimar em vez de um relatorio que voce pode ignorar.

Tooling e gates de CI que mantem tudo honesto

Sustentamos a modelagem manual com automacao para que regressoes nao reabram em silencio uma ameaca corrigida. O SAST roda com regras Semgrep custom que codificam os erros especificos que encontramos, como um check HMAC depois de um parse, e o SCA roda com osv-scanner contra a arvore de dependencias. Ambos sao gates bloqueantes para achados High e Critical no pipeline, de modo que um pull request que reintroduz uma fraqueza modelada quebra a CI em vez de shipar. Mantemos as regras Semgrep no mesmo repo que o servico, versionadas ao lado do codigo que protegem, e as revisamos quando um novo achado STRIDE sugere um padrao que vale pegar automaticamente. A automacao nao substitui a sessao humana, ela a torna cumulativa: a cada sprint o modelo manual encontra os problemas novos e as regras garantem que o sprint passado continue corrigido.

FAQ: 90 minutos bastam mesmo?

Para um servico com fronteira clara, sim, e a restricao e uma virtude, nao uma concessao. Uma caixa apertada obriga o time a modelar primeiro os fluxos que cruzam uma fronteira de confianca, onde de fato vivem os bugs exploraveis, e a adiar a analise de baixo valor de funcoes helper puramente internas. Se um servico e tao grande que 90 minutos nao cobrem seus fluxos que cruzam fronteira, isso e sinal de que o servico faz demais e deveria ser dividido, nao de que a sessao deva durar quatro horas. A cadencia recorrente e o que torna a caixa curta viavel: o que voce perde neste sprint voce pega no proximo, contra uma arquitetura que derivou apenas duas semanas em vez de seis meses.

FAQ: e se nao temos pesquisador ofensivo?

Voce pode rodar STRIDE sem um red-teamer dedicado, mas precisa importar deliberadamente a mentalidade adversarial que o pesquisador fornece, porque uma sala de construtores tende a modelar como o sistema deveria funcionar em vez de como ele quebra. Designe uma pessoa por sessao para bancar o atacante e a segure a exploracao concreta, pedindo "me de o request exato que abusa disso" em vez de aceitar "isso poderia ser arriscado". Semeie a sessao com um checklist das seis letras contra cada fluxo e uma biblioteca de achados passados para que as perguntas sejam estruturadas e nao improvisadas. E menos eficaz que um especialista ofensivo real, mas uma rotacao disciplinada do papel de atacante mais os gates automatizados recupera a maior parte do valor e cultiva o instinto de seguranca em todo o time com o tempo.

Conclusao: uma linguagem comum, nao um checklist de auditoria

STRIDE nao e um checklist de auditoria, e uma linguagem comum entre dev, SRE e ofensiva que transforma arquitetura numa lista priorizada de correcoes. A receita pratica e comecar com um DFD de uma pagina, forcar as seis letras contra cada fluxo que cruza uma fronteira de confianca, exigir que cada ameaca aterrisse como issue com criterio de aceitacao verificavel, e sustentar tudo com gates de SAST e SCA para que as ameacas corrigidas continuem corrigidas. Rode a cada sprint contra um escopo pequeno em vez de uma vez contra tudo, mantenha a sessao honesta com um papel de atacante, e meca sucesso por PRs mergeados em vez de paginas escritas. Se nao virar codigo neste sprint, nao foi threat modeling, foi teatro.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly