Modelagem de ameaças como código
Infrastructure-as-code (e GitOps) estendem o uso de controle de origem (git) e código (bem, arquivos de manifesto) em um novo campo. Isso mudou radicalmente a forma como criamos infraestrutura na nuvem, tornando o processo mais robusto e menos propenso a erros, e também mais fácil para os desenvolvedores. Podemos fazer o mesmo com a modelagem de ameaças? Como a modelagem de ameaças como código pode mudar e melhorar a maneira como fazemos a modelagem de ameaças hoje?
Vamos começar com uma breve introdução à modelagem de ameaças. A modelagem de ameaças é uma prática que nos ajuda a fazer um projeto de sistema e procurar possíveis problemas de segurança, fazendo estas 4 perguntas:
- O que estamos construindo?
- O que pode dar errado?
- O que estamos fazendo a respeito?
- Estamos fazendo um bom trabalho?
A condução de um modelo de ameaça ajuda a encontrar problemas mais cedo e, na maioria dos casos, a detectar problemas difíceis de encontrar usando outras práticas. É por isso que a condução de um modelo de ameaça é uma parte crítica na construção de um software secundário. Se você não está familiarizado com essa prática, recomendo muito este post de Adam Shostack, uma das autoridades na área. O projeto (e canal) OWASP Threat Modeling também é um excelente recurso de aprendizado.
Abordagem de infraestrutura como código
Eu gosto de trabalhar com o Git e, especialmente, com o fluxo de RP do GitHub. A ideia é simples: uma única ramificação mestre, que é mantida “verde” (por exemplo, todos os testes estão passando – ou seja, o código está funcionando conforme o esperado). Quando quero trabalhar em um recurso, crio uma ramificação, escrevo meu código e envio a ramificação. Então, eu posso abrir um pull-request (PR) – uma solicitação para puxar as alterações da minha ramificação para a ramificação principal. O que acontece em um PR?
- Testes estão sendo realizados para avaliar a qualidade das minhas mudanças. Com base nos resultados dos testes, a RP pode ser aprovada ou negada.
- Revisão de código. Alguém pode dar uma olhada no meu código e decidir se meu PR é aprovado ou não.
Esse fluxo é tão comum, que agora é usado para mais do que apenas código. Por exemplo, usando OctoDNS, todas as alterações de DNS são feitas pelo git. Isso permite que todos os desenvolvedores apliquem alterações de DNS e ainda se certifiquem de que ele permaneça seguro e protegido. Outro exemplo é o Terraform, que permite gerenciar a infraestrutura de nuvem como código, usando arquivos de manifesto para descrever os recursos necessários. Isso permite que você crie bancos de dados, servidores ou qualquer outra coisa apenas usando o git. Todos esses são exemplos de GitOps – operações usando git. O GitOps é tão popular e amado pelos desenvolvedores porque é implementado por ferramentas que são familiares e conhecidas pelos desenvolvedores.
fonte: Gil Zilberfeld
Modelagem de ameaças como código
Como tudo isso está relacionado à modelagem de ameaças? Imagine que poderíamos ter o mesmo para a modelagem de ameaças. Sempre que alguém quiser conduzir um modelo de ameaça, ela abrirá um PR com suas alterações em um repositório. No PR podemos:
- Executar testes: desde testes que validam o modelo de ameaça, até testes mais sofisticados que podem detectar problemas automáticos (“análise estática”). Imagine, por exemplo, que podemos adicionar uma ameaça de conexão de texto não criptografado a todas as conexões de rede. Talvez possamos até falhar no teste em ameaças específicas? Dizendo, se houver mesmo uma conexão que seja de texto claro, o PR está bloqueado? Tantas possibilidades aqui.
- Realizar uma revisão: o desenvolvedor pode pedir a muitas pessoas que revisem a modelagem de ameaças e adicionem seus insights. Isso pode acontecer de forma assíncrona ou como parte de uma reunião de modelagem de ameaças. O PR se torna o local onde as ameaças são documentadas. Mais tarde, podemos vincular os problemas que decidimos corrigir a este PR.
Mudar para uma modelagem de ameaças como código abre novas possibilidades. A principal motivação, pelo menos na minha opinião, é migrar para ferramentas que são usadas, amadas e conhecidas pelos desenvolvedores. Como? Deixe-me apresentar duas ferramentas que você pode começar a usar hoje para modelagem de ameaças como código.
PlantUML – desenho como código
A primeira ferramenta é o PlantUML, uma linguagem que permite criar um desenho, usando uma DSL (linguagem específica do domínio) específica. Tomemos por exemplo o seguinte “código” (este é o exemplo oficial daqui):
@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
Alice -> Bob: Another authentication Request
Alice <-- Bob: Another authentication Response
@enduml
Este código muito simples é traduzido para o seguinte diagrama:
PlantUML nos permite criar um desenho usando código (e tem muitos recursos para criar um diagrama complexo, incluindo numeração, tipo de participantes e muito mais). Isso nos livra de mexer com o desenho real do diagrama. E, como este é um código normal, podemos ter comentários de revisão sobre ele (veja um exemplo de RP aqui):
E os testes? A linguagem PlantUML é simples, escrever uma ferramenta SAST deve ser uma tarefa fácil. Já existem soluções existentes – por exemplo, o OWASP Threat Dragon tem um mecanismo de regras que pode gerar ameaças e mitigações automaticamente. A Ferramenta de Modelagem de Ameaças da Microsoft pode fazer o mesmo. Tudo o que precisamos é importar arquivos PlantUML para uma dessas ferramentas, e podemos facilmente ter testes.
E quanto ao templating? Na base, as coisas que construímos são geralmente semelhantes – uma API web que lida com solicitação HTTP, um trabalhador que lê de uma fila, aplicativo móvel que interage com uma API etc. Usando o PlantUML podemos criar modelos prontos, com o fluxo genérico e ameaças potenciais. Os desenvolvedores só precisam copiar o modelo, modificar de acordo com o caso de uso específico e revisar as ameaças. O PlantUML abre tantas possibilidades, apenas habilitando o diagrama como código.
Gherkin – threats-as-code
Desenhar um diagrama é apenas a primeira parte da condução de um modelo de ameaça. Depois de desenhá-lo, precisamos procurar possíveis ameaças, priorizá-las e discutir possíveis mitigações. Gherkin é uma linguagem para escrever histórias de usuários e cenários, e é comumente usada para Desenvolvimento Orientado ao Comportamento. Ameaças e controles são apenas histórias de usuários, então podemos usar o Gherkin para documentá-los. Um ótimo exemplo de uso do Gherkin para esse propósito é o OWASP Cloud Security Project (obrigado, Fraser Scott, pelo projeto e pela inspiração!). O objetivo do projeto é documentar ameaças e controles relevantes para aplicativos em execução em um ambiente de nuvem. Vejamos um exemplo de ameaça:
Scenario: Getting the security credentials
When the attacker injects a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
Then the temporary security credentials for ROLE_NAME are returned
E este é o controle correspondente, para mitigar essa ameaça:
Scenario: Application is protected against Server Side Request Forgery
Given an EC2 instance with access to the metadata service
And an application running on the instance
When we inject a request to the http:/169.254.169.254 metadata service URL
Then the application must not call the provided metadata service URL
And the application must not return any results of a call to the metadata service
Usar a linguagem Gherkin (e mais em geral, usar histórias de usuários) para documentar ameaças e controle torna muito mais fácil entendê-las e priorizá-las. Como no PlantUML, adicionar uma revisão é simples - este é apenas um arquivo de texto.
E os testes? O Gherkin é comumente usado para Desenvolvimento Orientado ao Comportamento: Escrevendo um cenário usando o Gherkin e tendo um código que gera testes automaticamente a partir desse cenário. Por exemplo, para a ameaça acima – o código deve gerar um teste para vulnerabilidade SSRF na API. Há algum desenvolvimento nesta área ([Threat Playbook](https://en.wikipedia.org/wiki/Behavior-driven_development) é um ótimo exemplo de testes de segurança automáticos), e espero que vejamos mais ferramentas como essa.
Usando o Gherkin, também podemos criar bibliotecas de ameaças facilmente (como o Cloud Security Project, que mencionei anteriormente). Os desenvolvedores podem consultar a biblioteca ao conduzir um modelo de ameaça e escolher as ameaças relevantes para seu caso de uso. A biblioteca deve conter controles comuns para mitigar essas ameaças – portanto, tudo o que resta é apenas escolher o controle certo e implementá-lo.
Conclusão
Quando trabalhei no lançamento do Kamus, nossa solução de gerenciamento secreto para o Kubernetes, olhei como seria melhor lançar publicamente o modelo de ameaças – a fim de compartilhar a ameaça e as mitigações que foram discutidas. Combinar o poder do PlantUML e Gherkin foi uma ótima escolha para este caso de uso (ainda estou procurando uma boa solução para gerar um site). Este é outro benefício da modelagem de ameaças como código, e espero ver mais projetos de código aberto seguindo esse caminho no futuro.
Usar PlantUML e Gherkin é apenas o primeiro passo para a modelagem de ameaças como código. Pela minha experiência, isso facilita um pouco para os devs, mas as ferramentas ainda não estão maduras o suficiente. Ter algum tipo de teste automatizado pode aumentar a experiência – e, com sorte, tornar a modelagem de ameaças mais fácil para todos. A modelagem de ameaças é um processo crítico de construção de software seguro, torná-lo mais fácil (e talvez até divertido?) poderia encorajar mais desenvolvedores a adotá-lo.
Editar:
Acabei de descobrir que o Abhay Bhargav deu uma palestra sobre o mesmo assunto na AppSec USA. Obrigado, Josh Grossman por compartilhar esta palestra!