Padrões para endurecimento do processo de geração de código
Recentemente eu caí em um vídeo na internet que fala sobre como LLMs avançaram substancialmente em testes de penetração de código.
O vídeo, para além de bastante interessante, aborda um tópico que o próprio pesquisador da Anthropic Nicholas Carlini fica estarrecido. Se todos falamos sobre a inovação da IA (e de como ela pode ser boa) quem fala da segurança daquilo que já existe e funciona?
Pois bem, como eu gosto do tema e da interseção deles, eu decidi destinar um pouco dos minutos do meu dia a tentar dar um norte para aqueles que podem estar bastante surpresos ou que tenham paranoias com agentes "superinteligentes" que podem encontrar vulnerabilidades 0-day em grandes bases de código.
Programação em pares
Eu acredito piamente que pair programming pode ser aplicado em todas as etapas seguintes e sempre acreditei que múltiplos olhos enxergam melhor o problema. A Brigitta Böckeler tem uma publicação que fala exatamente sobre isso.
A ideia principal é juntar desenvolvedores (ou dev/QA, dev/UX, dev/SRE, por que não?) e discutir sobre a implementação de uma porção nova do código e sobre como isso vai impactar o sistema, assim como implementar o código e discutir melhores práticas, trazendo a expertise de cada pessoa para o problema.
Desenvolvimento orientado a testes (TDD)
Muitas pessoas acham que TDD é um assunto batido; entretanto, durante meu período trabalhando com cibersegurança, entendi que testes são a tua primeira rede de proteção contra possíveis erros cometidos (não só por ti, mas por um LLM também). O Leandro Proença tem comentários bem legais sobre vibe-engineering (acho melhor chamar assim) e como contornar os possíveis erros gerados pela alucinação inerente dos algoritmos.
Testes podem funcionar como uma contrato a ser seguido por classes, funções, módulos e até serviços. Ao usar o ciclo do TDD (testar - falhar - refatorar) também nos acostumamos a validar diferentes casos de uso, ampliando a nossa visão sobre a aplicação.
O Jean Jacques tem uns insights muito bons sobre coberturas de testes e boas práticas com uso de machine-learning para geração de código. Recomendo bastante a leitura.
Validação de Dados
Na área de dados é muito comum falar sobre data contracts e data lineage durante os processos de implementação de ETLs e produtização desses dados.
E, assim como a área de dados usa muitas coisas que temos na área de software, também acho que podemos aprender com eles, especialmente nessa discussão.
Data contracts:
Refere-se a um padrão para identicar todas as propriedades que são inerentes àquele conjunto de dados. O contrato pode conter informações sobre: o sistema (versão da api, status, versão do contrato, origem e destino do dado…), um dicionário de dados (tipificação das tabelas de dados usadas para compor o contrato), informações sobre qualidade de dados (certificação de valores, contagem de linhas e colunas e validação da tabela), o time que documentou o contrato… E muitas outras informações que podem ser revistas aqui.
Data Lineage:
Refere-se ao processo de mapeamento dos dados, desde a criação até a execução deles. Esse processo ajuda a ter um controle e métricas que podem ajudar na rastreabilidade e monitoramento da sua aplicação, que inclusive pode deixar sua estratégia de logs mais robustas.
Threat Modelling
De todas as sugestões que eu tenho para dar acho que essa é a mais impactante.
Threat Modelling refere-se ao processo de mapear possíveis falhas através de quatro perguntas feitas durante a concepção da aplicação ou a cada nova revisão ou implementação.
O processo envolve essencialmente 4 etapas.
Escopo da aplicação
Aqui estamos interessados no maior detalhamento possível da aplicação. Uma ótima forma de enxergar os componentes presentes é através de técnicas visuais, então diagramas, quadros e desenhos são bem vindos. Também é importante que se identifique pontos de entrada e saída de dados no sistema e crie fluxo de como esses dados são manipulados. Outro ponto importante, ainda nessa etapa, é a identifação de privilégios (permissões) que a aplicação concede ou requisita de outros sistemas para o pleno funcionamento. Devido a grande quantidade de pontos a serem cobertos, uma história de usuário pode ajudar e muito o mapeamento.
Identifação de possíveis ameaças
Para identifação de ameaças relacionadas a aplicação, frameworks como o STRIDE são frequentemente utilizados e com adoção fortemente encorajada por muitas organizações. Para times que lidam com operações, ATT&CK e Cyber Kill-Chain também são utilizados para mapear possíveis rotas de ataque ou comportamento de possíveis atacantes.
Mitigação de incidentes
Aqui é outra parte da discussão que pode render mais debates. Se o problema ocorre na forma de supply-chain, de quem é a responsabilidade por lidar com o incidente? A empresa que usa a ferramenta ou a equipe que mantém? Uma forma eficiente de mapear a mitigação é entender o tamanho do impacto operacional que cada ameaça mapeada anteriormente oferece ao sistema. Mas em linhas gerais existem quatro opções:
- Aceitar: o possível impacto causado pela vulnerabilidade é aceitável.
- Eliminar: remover os componentes que podem ser responsáveis pela vulnerabilidade.
- Mitigar: adicionar controles de fluxo que podem reduzir o impacto causado pela vulnerabilidade (logs, rastreio de endereços IP, restrição de acesso…)
- Transferir: transferir o impacto para outras partes do fluxo.
Revisão
Depois de todo o processo criado e determinado, uma boa prática é revisar. Quando o sistema é muito extenso; mas bem documentado, uma outra prática é utilizar IA para verificar se o planejamento está correto (lembrando sempre das especificações necessárias para que o sistema tenha suas própias restrições), mas mantendo um olho sempre crítico sobre o que a IA gera.
Conclusão
Em linhas gerais, falar de segurança é um trabalho extremamente multidisciplinar e que envolve diferentes faces do fluxo de desenvolvimento de software. Mas ainda que seja um trabalho minucioso, assegurar os bens digitais é fundamental para o bom funcionamento da web.
Testes são fundamentais, e boas práticas de aquitetura são imprescindíveis para uma boa manutentabilidade do código a longo prazo. Com o advento do uso de machine learning de forma rotineira nas nossas aplicações e a possibilidade dessas ferramentas gerarem código inseguro, é também importante que se remova componentes inseguros dos algoritmos.
Uma boa implementação de machine learning depende de projetos bem estruturados e com fluxos bem mapeados. A Loiane Groner tem um artigo bastante interessante sobre spec-driven development e como essa nova forma de desenvolvimento ajuda na inércia e mantém a qualidade sem diminuir a velocidade de entrega.
É importante salientar que não existe bala de prata quando se fala de segurança e que nem toda falha é o fim do mundo; entretanto é necessário que se dê a devida atenção aos processos e componentes neles presentes.
Apreciação
Obrigado pela leitura até o final, essa a primeira publicação de uma série de outras que pretendo trazer aqui, se você gostou ou gostaria de deixar uma sugestão pode me contatar pelo LinkedIn ou me mandar um email através de joaoaugustolaner[at]proton[dot]me