Cartão de título de 'Security Code Review Explained' mostrando um painel de diff de código com uma linha original e uma linha alterada, e três etapas rotuladas abaixo dele: PADRÕES (determinístico), depois PASSA, depois RACIOCÍNIO (modelo).
Guides & Insights

Revisão de Segurança de Código Explicada: O Que um Revisor Procura em um Diff, e Por Quê

Autor

Rowan Sterling

Data de publicação

Modelos mais recentes · 20Ver todos os modelos →
Benchmarks: Artificial Analysis · atualizado diariamente
Voltar para todas as publicações

Uma revisão de código de segurança é a leitura deliberada do código-fonte com o propósito específico de encontrar maneiras de fazê-lo fazer algo que seu autor não pretendia. Essa definição parece óbvia até você compará-la com o que a maioria das equipes realmente executa, que é uma revisão funcional com uma lista de verificação de segurança por perto. O OWASP Secure Code Review Cheat Sheet traça a linha com precisão: uma revisão de segurança difere de uma revisão funcional por ter como alvo validação de entrada, autenticação, autorização, implementação criptográfica e vetores de ataque, em vez de correção. Um diff que funciona perfeitamente ainda pode entregar a um atacante um caminho para dentro do sistema, e apenas uma dessas duas revisões está procurando por isso.

O que torna o tema digno de um artigo agora, e não em 2020, é que um modelo de linguagem consegue ler um diff de forma competente, barata e contínua — o que muda a economia da revisão sem mudar uma única das dimensões que estão sendo revisadas. A checklist é mais antiga que os modelos e não mudou. O que mudou é a frequência com que você pode se dar ao luxo de executá-la.

O que um revisor realmente busca ao ler

A folha de dicas da OWASP organiza o trabalho em seis áreas, e o ponto útil da lista é que cada área é uma pergunta, e não uma categoria. Categorias convidam a marcar caixinhas; perguntas produzem achados.

Card 'What a reviewer is actually looking for' listing six dimensions - input and injection, authn and session, authorization, cryptography, business logic, configuration and secrets - each with the question it answers about a diff.

Entrada e injeção. A questão não é se o código tem um sanitizador; é se algo chega a um interpretador — shell, SQL, mecanismo de template, avaliador de expressões — e em qual camada essa exigência deve ser aplicada. Um diff que adiciona uma consulta parametrizada é bom; um diff que adiciona uma consulta parametrizada a uma função cujos outros chamadores passam strings brutas é um diff que moveu o problema.

Autenticação e sessão. Quem é o chamador e como é que o código estabeleceu isso? O bug recorrente não é uma verificação de palavra-passe avariada, é uma sessão que sobrevive a uma mudança de privilégios — a elevação acontece, o token mantém as suas declarações antigas e, uma hora depois, os privilégios continuam lá.

Autorização.Verificações em nível de objeto, sempre, em todas as rotas. O detalhe específico a procurar em um diff é uma nova rota que não herda a guarda que as rotas irmãs têm, porque o middleware é registrado por roteador e uma rota adicionada sob um diferente simplesmente não será coberta.

Criptografia. Primitiva, modo, comprimento da chave e onde a chave fica armazenada. Duas perguntas capturam a maioria dos achados reais: qualquer pessoa que consiga ler esta base de código também consegue ler a chave, e esta comparação é uma que não deve vazar timing.

Lógica de negócio. Se a sequência pode ser reordenada, reproduzida ou determinada por valores escolhidos pelo cliente. Um preço, uma quantidade, um papel ou uma transição de estado que chega do cliente e não é recalculado no servidor é um achado, independentemente de como seja validado.

Configuração e segredos. O que o diff traz que nunca deveria estar em um repositório, e se os padrões são seguros onde o código realmente é executado. Um sinalizador de depuração cujo padrão é true é um achado sobre a produção, não sobre o sinalizador.

Revisão de linha de base versus revisão de diff

O OWASP separa dois modos, e confundi-los é uma fonte comum tanto de esforço desperdiçado quanto de falsa confiança. Uma revisão de linha de base examina a base de código de forma abrangente, e é a ferramenta certa para um novo aplicativo ou uma versão principal, integração de um sistema legado, atendimento a um requisito de conformidade ou realização da análise pós-incidente depois que algo já deu errado. Uma revisão baseada em diff analisa apenas o que mudou, e é a ferramenta certa para pull requests, commits, conclusão de funcionalidades e validação contínua.

A distinção é importante porque os dois produzem trabalho diferente. Uma revisão de linha de base termina com um backlog; uma revisão de diff termina com uma decisão de merge. Executar a primeira continuamente é inviável, e executar a segunda em uma base de código que nunca teve a primeira é como as equipes acumulam uma falsa sensação de cobertura: toda alteração passa pela revisão, e a concatenação de SQL preexistente permanece intocada.

Screenshot of the OWASP Cheat Sheet Series index entry for Secure Code Review, showing the introduction describing manual examination of source code for vulnerabilities automated tools miss, the baseline-versus-diff review split with their use cases, and a table of contents covering methodology, patterns, techniques, checklists and tools.

Técnicas, e o que cada uma detecta

A OWASP lista quatro técnicas de revisão, e estas distinguem-se pelo tipo de evidência que o revisor segue, e não pelo seu grau de minúcia. Análise de padrões de códigosegue formas familiares: a chamada de função perigosa, a desserialização de entrada não confiável, a verificação de comprimento em falta. É rápida e é exatamente o que uma ferramenta determinística faz bem. Análise de fluxo de dadossegue um valor desde o ponto em que entra até ao ponto em que é utilizado, que é a única forma de responder se o sanitizador aplicado três funções atrás ainda se aplica aqui. Revisão baseada em ameaçasparte da capacidade de um atacante e avança a partir daí, que é como se deteta a lacuna entre dois componentes individualmente corretos. Revisão da lógica de negóciolê o fluxo de trabalho em vez das linhas, perguntando que sequência de operações legítimas produz um estado ilegítimo.

Apenas o primeiro desses quatro é bem atendido por um scanner, e apenas o primeiro e o segundo são bem atendidos por uma execução do modelo sem orientação. Isso não é um argumento contra modelos; é um argumento para dizer ao revisor qual técnica você quer, porque um prompt que pede “problemas de segurança” vai produzir, de forma confiável, uma análise de padrões e parar.

O que a IA mudou, e o que não mudou

A pesquisa sobre código escrito por IA tornou o gargalo da revisão mais acentuado, em vez de criá-lo. O estudo da NYU sobre sugestões do GitHub Copilot, publicado no IEEE S&P em 2022, descobriu que cerca de 40% dos trechos gerados continham um padrão explorável em 18 categorias CWE. O trabalho de Stanford sobre o comportamento de desenvolvedores, publicado no ACM CCS em 2023, constatou o efeito de maior consequência: desenvolvedores que usavam assistentes estavam mais confiantes de que seu código era seguro, enquanto produziam código mensuravelmente menos seguro. Mais volume de código somado a mais confiança é um problema de revisão.

Um modelo de linguagem como revisor altera uma variável nessa equação — a vazão. Ele pode ler cada pull request, a um custo por token em vez de por usuário, e pode receber explicitamente as seis perguntas acima. Ele não altera as perguntas e não adquire as duas propriedades que tornam uma revisão confiável: determinismo e a capacidade de executar o código. O mesmo diff revisado duas vezes pode produzir constatações diferentes, as severidades mudam entre execuções, e o revisor não consegue provar que um caminho sinalizado é alcançável.

Screenshot of the OrcaCode Review product page on OrcaRouter headed 'Open engine. Open prompt. Open bill.', describing a pull-request reviewer whose rules and merge gate run on GitHub under an MIT licence and are billed per token, with an example finding about an idempotency key that still varies in time.

OrcaCode Review é a forma productizada dessa ideia, e vale a pena examiná-la de perto porque as escolhas de design revelam os modos de falha. Uma revisão que isola o caso de falha como um comentário específico e reproduzível — esta chave ainda varia no tempo, então uma cobrança repetida gera uma nova chave e pode cobrar em dobro, aqui está a alteração de uma linha — está a fazer o trabalho que um revisor faz. Uma revisão que relata "potencial problema de segurança" sem uma reprodução está a fazer análise de padrões com pior precisão do que o próprio correspondedor de padrões. E um motor de regras e portões publicado sob uma licença aberta está a fazer uma afirmação implícita que vale a pena verificar: que o julgamento do revisor também deve ser sujeito a revisão.

O arranjo de duas camadas que funciona

Dado um scanner determinístico e um revisor probabilístico, o arranjo que se sustenta na prática é atribuir a cada um o trabalho para o qual ele é realmente bom. O scanner é executado a cada push: segundos, sem modelo, sem variação, capturando segredos, chamadas de API perigosas, desserialização insegura e imports alucinados. O revisor é executado a cada pull request: minutos, baseado em modelo, capturando a lógica nas entrelinhas — se a verificação de autorização é executada antes da busca, se a chave de idempotência está vinculada a um valor estável, se a nova rota herdou a proteção.

Em seguida, a camada que as pessoas ignoram: o portão. Um achado que não bloqueia um merge é uma notificação, e notificações são lidas às sextas-feiras. O portão tem de ser estreito e tem de ser absoluto — achados críticos bloqueiam, todo o resto vira comentário — e a isenção tem de ser registrada por escrito, porque o valor de um portão não está em impedir o primeiro achado, mas em tornar visível cada desvio.

Revisando a alteração, não a base de código

A disciplina prática para uma revisão de diff é uma sequência curta, e seu valor é que pode ser ensinada em uma tarde. Leia o diff pelo que ele acrescenta à superfície de ataque, e não pelo que ele muda. Para cada novo ponto de entrada, siga a entrada um salto além de onde o diff termina — a vulnerabilidade geralmente está na função que ele chama, que o autor não leu. Para cada nova rota, verifique o registro do guard. Para cada nova dependência, verifique se ela existe e se outra pessoa publicou o nome. Para cada novo valor de configuração, pergunte qual é o padrão dele onde ele realmente será implantado.

Nada disso exige conhecer bem o idioma, e é por isso que escala para revisores que não são especialistas em segurança. Exige, porém, que o revisor veja o diff e não o arquivo, porque todo o método depende de saber o que é novo.

E o limite honesto: uma revisão de diff não consegue encontrar a falha que já estava ali antes do diff. Se a aplicação nunca passou por uma revisão de linha de base, o trabalho de segurança de maior valor disponível não é um revisor melhor — é uma passada deliberada pelo código, nas áreas que envolvem dinheiro, identidade e acesso a arquivos, com as seis perguntas acima. Depois disso, toda revisão de diff subsequente está protegendo algo que já foi examinado uma vez. Antes disso, toda revisão de diff está protegendo um desconhecido.