A maioria dos threat models é escrita uma vez. Uma equipa marca um workshop, desenha o sistema, lista ameaças com STRIDE e produz um documento. Muitas vezes é um bom documento. Seis meses depois o produto tem novas integrações, um novo caminho de deploy e dois novos papéis, e o documento descreve um sistema que já não existe.
A nossa posição é que o problema não é disciplina. É forma. Um único documento mistura coisas que mudam a velocidades muito diferentes e que pertencem a pessoas diferentes. Divida-o em camadas e ele consegue acompanhar.
Porque é que um só documento falha
Pense em quem sabe o quê numa organização típica. O produto sabe que fluxos movimentam dinheiro e que dados são sensíveis. Os arquitetos conhecem os serviços e as fronteiras. Os programadores conhecem os caminhos do código. A plataforma conhece o pipeline e os seus tokens. As operações sabem o que está realmente a correr e quem lhe consegue chegar.
Um único threat model pede a todos que atualizem um só artefacto. Na prática, ninguém o faz, porque nenhuma alteração parece grande o suficiente para reabrir tudo. Assim, o modelo vai-se desatualizando até a próxima auditoria ou incidente obrigar a reescrevê-lo.
Há um segundo problema. Uma ameaça num nível tem muitas vezes a causa noutro. Um bug de autorização no código pode existir porque o design nunca disse quem é dono de um objeto. Um risco de cadeia de fornecimento no pipeline pode só importar porque o runtime tem permissões de cloud alargadas. Uma lista plana de ameaças esconde essas ligações.
As cinco camadas
Mantemos os threat models em cinco camadas. Cada uma tem um dono e um gatilho claro para atualização:
| Camada | O que guarda | Dono | Atualizar quando |
|---|---|---|---|
| Negócio e processo | Ativos valiosos, fluxos críticos, objetivos de abuso, atores | Produto | Uma nova funcionalidade, mercado ou função |
| Arquitetura | Serviços, armazenamentos de dados, fronteiras de confiança, fluxos de dados | Arquitetos ou tech leads | Um novo serviço, integração ou store |
| Código | Entry points, lógica de autorização, parsers, uso de criptografia | Equipas de serviço | Alterações a paths sensíveis |
| Pipeline | Runners, tokens, actions, dependências, assinatura | Plataforma | Um novo workflow, segredo ou destino de deploy |
| Runtime | Identidades, alcance de rede, portas expostas, deteção | Operações ou SRE | Nova infraestrutura ou alterações de permissões |
Cada camada é pequena o suficiente para ser revista numa hora, e cada uma muda quando o seu responsável já tem o contexto na cabeça.
Ameaças que atravessam camadas
As camadas não são documentos separados que se ignoram uns aos outros. O valor está nas ligações. Uma ameaça é registada na camada onde se concretiza e ligada às camadas que a permitem ou limitam. Por exemplo:
- NegócioObjetivo: um cliente lê os extratos de outro cliente.
- ArquiteturaO serviço de extratos partilha uma base de dados entre tenants, filtrada por um tenant id.
- CódigoO endpoint de exportação recebe um id de extrato e tem de verificar o seu tenant em cada chamada.
- RuntimeAs chamadas de exportação ficam registadas com tenant e ator, e geram alertas perante padrões entre tenants.
Agora, uma alteração em qualquer camada diz-lhe o que verificar de novo. Se a arquitetura passar a bases de dados por tenant, a verificação ao nível do código passa a ser defesa em profundidade em vez do único controlo. Se alguém acrescentar uma exportação em massa, o dono da camada de código vê que um objetivo da camada de negócio depende dela.
As objeções habituais
"Isto é mais trabalho." É menos trabalho, distribuído de forma mais uniforme. Atualizar uma camada depois de uma alteração leva minutos. Reescrever um modelo inteiro todos os anos leva dias e nunca fica bem acabado.
"As nossas equipas não vão manter isto." Não vão manter um documento. Vão manter uma secção que é acionada por alterações que já fazem, sobretudo se o template de pull request fizer a pergunta.
"Não temos uma equipa de segurança para gerir isto." As camadas pertencem às pessoas que as conhecem. A segurança, se existir, revê as ligações e as lacunas, que é onde a experiência mais conta.
O que faríamos
- Comece pela camada de negócio. Cinco a dez objetivos de abuso em linguagem simples chegam.
- Desenhe a camada de arquitetura numa página, com as fronteiras de confiança marcadas.
- Para código, pipeline e runtime, comece apenas pelas partes ligadas a esses objetivos. Não tente modelar tudo.
- Guarde cada camada como texto no repositório mais próximo do seu dono e ligue-as por id.
- Adicione uma linha aos templates de pull request e de alteração: "Esta alteração muda uma camada do threat model?"
- Reveja as ligações uma vez por trimestre. Objetivos sem ligação e controlos órfãos são a agenda.
Esta é a ideia por trás do layered threat model que estamos a construir na torre: cada camada atualiza-se a partir dos sistemas que já a descrevem, e as ligações mostram o que uma alteração afeta. Mas não precisa de uma plataforma para começar. Uma pasta de ficheiros de texto curtos e um hábito levam-no a maior parte do caminho ao longo da linha.
Onde isto fica na linha
Na torre (em desenvolvimento):