O discurso de agradecimento mais alarmante da computação

e receberam o Prêmio Turing de 1983 pelo . Quem faz a palestra Turing costuma refletir sobre uma carreira. A de Thompson, publicada na Communications of the ACM em agosto de 1984, começa dizendo que ele sente estar recebendo a honraria tanto por oportunidade e acaso quanto por mérito técnico - e então descreve, em poucas páginas, um jeito de comprometer todo programa de um sistema que nenhuma revisão de código-fonte conseguiria detectar.

Ele o construiu em três estágios, e o terceiro é o que importa.

O primeiro estágio é um programa que imprime o próprio código-fonte. É uma velha curiosidade de programação e não faz mal nenhum; está na palestra para estabelecer que um programa pode conter uma descrição completa de si mesmo.

O segundo estágio é um compilador ensinado a reconhecer um trecho específico de código-fonte - o programa login - e, ao vê-lo, compilar junto uma porta dos fundos que aceita uma senha escolhida por Thompson. Quem lesse o fonte do login não acharia nada, porque não há nada lá. A porta é acrescentada durante a compilação.

O terceiro estágio é o movimento que a torna permanente. O compilador também é ensinado a reconhecer quando está compilando a si mesmo, e a reinserir os dois truques - a porta no login e o autorreconhecimento - no novo binário de compilador que produz.

Uma vez que esse compilador exista como binário, o fonte malicioso pode ser apagado. Dali em diante o fonte do compilador é limpo e sempre será. Toda vez que alguém reconstruir o compilador a partir desse fonte limpo, usando o compilador existente, o novo binário é infectado de novo. O defeito mora no binário, é invisível em toda listagem de fonte, e é passado adiante indefinidamente.

A moral do próprio Thompson é a frase que todo mundo cita: não se pode confiar em código que você não criou inteiramente. O parêntese dele é o que as pessoas esquecem, e é melhor - especialmente código de empresas que empregam gente como eu.

Ele fez isso de verdade?

Fez, no sentido de que escreveu; chamou aquilo do programa mais gracinha que já escreveu. Se chegou a escapar é pergunta que as pessoas continuaram fazendo, e em 2011 alguém simplesmente mandou um e-mail perguntando. A resposta de Thompson foi que nunca foi para o mundo real.

Essa é a palavra dele, que é o que o registro contém, e é o lugar apropriado para deixar a questão. O ponto da palestra nunca foi que aquele compilador específico estivesse por aí. Foi que o ataque é possível, barato e indetectável pelo método que todo mundo usa para estabelecer confiança.

Por que revisar o fonte não salva você

Esta é a parte a levar a qualquer discussão sobre código aberto e auditoria, e precisa ser dita com precisão, porque é fácil exagerar nas duas direções.

Código aberto significa que o fonte pode ser lido. Não significa que o binário que você está rodando corresponde ao fonte que foi lido. Entre os dois estão um compilador, um ligador, um ambiente de compilação, um sistema operacional e uma cadeia de ferramentas, cada uma delas também compilada por alguma coisa. O ataque de Thompson mora nessa lacuna, e a lacuna existe seja o projeto aberto ou fechado.

Note também que a mesma lacuna derrota a linha de defesa seguinte. O artigo sobre código perfeito registra o caso Juniper, em que uma constante alterada num gerador de números aleatórios produzia saída criptograficamente indistinguível da original - então testar o comportamento do produto também não teria achado. A revisão de fonte falha contra Thompson; o teste comportamental falha contra a Juniper. O que resta é procedência: você consegue estabelecer que este binário veio daquele fonte?

O único método que responde formalmente

Por vinte anos a resposta honesta foi que não se conseguia, e a regressão de Thompson parecia infinita: para confiar no compilador é preciso confiar no compilador que o construiu, e assim para trás, para sempre.

David A. Wheeler quebrou isso. Apresentou a Diverse Double-Compiling na conferência ACSAC em 2005 e a expandiu numa tese de doutorado na George Mason University em 2009, intitulada Fully Countering Trusting Trust through Diverse Double-Compiling. O enquadramento do próprio Wheeler sobre o que está em jogo é direto: as contramedidas conhecidas eram grosseiramente inadequadas, e se o ataque não pudesse ser combatido, atacantes poderiam subverter em silêncio classes inteiras de sistemas e ganhar controle completo sobre sistemas financeiros, de infraestrutura, militares e de negócios no mundo todo.

A sacada é usar o determinismo do compilador contra ele. Compile o fonte do compilador com um segundo compilador, inteiramente diferente - que não tem razão para compartilhar a infecção do primeiro. O resultado é um compilador funcionalmente equivalente, mas construído de outro jeito. Agora use esse para compilar o fonte do compilador original de novo. Se o compilador original for honesto, o binário produzido bate com aquele de que você partiu. Se ele carregava o truque de Thompson, não bate, porque o segundo compilador, confiável, não tinha instrução de reinserir nada.

Continua sendo, até onde a literatura registra, o primeiro e único método que fecha formalmente a regressão. Wheeler também aponta que Thompson não foi o primeiro a levantar a questão: uma avaliação da Força Aérea sobre o Multics fizera a mesma observação antes.

O descendente moderno: builds reproduzíveis

A dupla compilação diversa é rigorosa e raramente executada. Sua prima prática hoje é difundida e vale entender como a resposta operacional.

Um build reproduzível é aquele em que o mesmo fonte, construído por qualquer um, a qualquer tempo, em qualquer máquina conforme, produz um artefato idêntico bit a bit. Essa propriedade soa banal e não é. Se um build é reproduzível, muitas partes independentes podem construir o mesmo fonte e comparar resumos criptográficos, e uma divergência prova que uma delas foi comprometida. Converte a pergunta sem resposta - minha cadeia de ferramentas é honesta? - numa pergunta respondível: builds independentes suficientes concordam?

Chegar lá é engenharia sem glamour: remover carimbos de tempo, ordenar arquivos, eliminar caminhos de compilação e nomes de máquina da saída, fixar a versão de toda dependência. Debian, Arch, Tails, Bitcoin Core e um número crescente de projetos fazem isso. A maior parte do software não faz, e é por isso que a seção seguinte é possível.

Por que este é o ancestral da taxonomia de cadeia de suprimento

Releia Thompson e toda rota da taxonomia de cadeia de suprimento é uma variação da mesma observação: a coisa que você revisou não é a coisa que rodou.

O XZ Utils é o parente moderno mais próximo. A porta dos fundos não estava no repositório git que todos podiam ler - estava no tarball de lançamento, em scripts de compilação, ativada apenas quando o pacote era construído para uma distribuição. Quem auditasse o fonte no próprio site do projeto não acharia nada errado, exatamente como Thompson especificou.

A 3CX é a outra metade: o fonte estava bem e o ambiente de compilação fora comprometido, então a saída era maliciosa e corretamente assinada. A assinatura atestava que o binário viera do pipeline que dizia; o pipeline era justamente a coisa que fora tomada.

E o caso do ScreenOS da Juniper é a versão criptográfica, em que nem a saída era distinguível da correta.

O que um profissional deve de fato fazer

Ninguém vai fazer dupla compilação diversa na própria cadeia de ferramentas, e fingir o contrário desperdiça o tempo de quem lê. As práticas transferíveis são menores e reais.

Saiba a partir de quê você constrói, e confira se bate com o que o upstream publicou. A porta do XZ estava no tarball e não no repositório; comparar os dois teria mostrado a diferença. Pouquíssima gente compara.

Prefira projetos que produzem builds reproduzíveis, e diga isso nas perguntas de compra. É uma propriedade concreta e verificável, ao contrário da maior parte das alegações de segurança de uma ficha técnica.

Trate o sistema de build como infraestrutura de produção, porque é. Ele guarda chaves de assinatura, tem acesso de rede, roda código de dependências, e um comprometimento dele é um comprometimento de tudo que ele já entregou. A maioria das organizações protege os servidores de produção e deixa o servidor de build com a equipe que precisou dele.

E lembre a que pergunta uma assinatura responde. Ela diz que um binário saiu de um pipeline específico. Não diz absolutamente nada sobre se o pipeline era honesto - que é a frase que Thompson escreveu em 1984, reenunciada no vocabulário de 2026.

Fontes