Continuação do artigo “Digitalizando contratos do Ethereum ao vivo para o erro de envio não verificado”. Parte 1 " .
Quase um ano atrás (enquanto o Ethereum estava em seu lançamento "de fronteira"), o popular contrato de loteria EtherPot [9] também sofreu com o mesmo erro. Uma versão anterior do BTCRelay também mostrou esse erro [7] . Embora um perigo tenha sido detectado em uma auditoria de segurança anterior, uma correção incorreta foi aplicada primeiro [8] .
Detecção de erro de envio não verificado no blockhain ativo
Quão comuns são esses erros? Eles ouvem avisos? As melhores práticas são aplicadas? Respondemos a essas perguntas empiricamente analisando os dados da cadeia de blocos Ethereum, bem como o repositório de códigos Solidity encontrado em etherscrape.com. Para isso, estamos desenvolvendo uma ferramenta simples de análise de programa que verifica o contrato da cadeia de blocos e usa heurísticas para verificar se um dos métodos de proteção mais eficazes é usado. A Listagem 2 mostra a primeira técnica de segurança, conforme recomendado na documentação do Ethereum, que deve verificar o valor de retorno de send e lançar uma exceção. Para detectar o uso desse método, usamos uma aproximação aproximada: apenas verificamos se o valor de retorno do envio é ignorado ou não.
A Listagem 4 ilustra a segunda técnica de segurança recomendada no manual do UMD, que verifica diretamente se a pilha de chamadas está cheia enviando uma mensagem de teste. Para descobrir essa técnica, usamos novamente uma aproximação aproximada: apenas verificamos se uma mensagem está sendo enviada além do comando send .
Se nenhum desses indicadores heurísticos estiver presente, concluímos que nenhuma das recomendações de melhores práticas está sendo seguida. Implementamos essas heurísticas usando a correspondência simples de padrões com o bytecode compilado do EVM. Mais detalhes sobre como fazemos isso podem ser encontrados no Apêndice [12] .
Quantos contratos são vulneráveis?
Vamos começar verificando a heurística no repositório Etherscrape do código fonte do Solidity. Em 20 de março de 2016, o relé Etherscrape continha 361 programas de contratos Solidity, 56 dos quais continham uma declaração de envio. Desses programas contratuais, assumimos que a maioria (pelo menos 36 de 56) não usa nenhum dos métodos de programação defensiva.
Mesmo que o contrato não use nenhuma das tecnologias de proteção, ele pode ou não ter uma vulnerabilidade real. Verificamos manualmente os contratos do Solidity para confirmar a vulnerabilidade. Para nossos propósitos, consideramos um contrato vulnerável se seu estado puder mudar mesmo que o comando send não funcione (portanto, veremos o código vulnerável na Listagem 5). Confirmamos que a grande maioria das vulnerabilidades está presente, 32 dos 36 desses contratos.
Da mesma forma, nossa heurística não garante a correta aplicação da programação defensiva. Tomemos, por exemplo, o WeiFund, um DApp descentralizado de crowdfunding de código aberto. Este contrato tem duas funções: reembolso () e pagamento () , que enganam nossa heurística. A seguir, um trecho do reembolso .
function refund(uint _campaignID, uint contributionID) public { ... receiver.send(donation.amountContributed); donation.refunded = true; ... if(c.config != address(0)) WeiFundConfig(c.config).refund(_campaignID, donation.contributor, donation.amountContributed); }
Nesse código, uma mensagem é enviada ao WeiFundConfig (c.config) para chamar o método de reembolso, mas apenas sob certas condições. Se c.config for um valor nulo, o contrato estará realmente vulnerável a um ataque de pilha de chamada. Ao marcar *, nenhum dos programas Solidity que passaram no teste heurístico realmente aplicou diretamente a melhor prática recomendada de teste de pilha de chamada. *
Em seguida, voltamos nossa atenção para os contratos elaborados na cadeia de blocos viva Ethereum. Vimos a imagem de 20 de março de 2016 (carimbo de data / hora: 1184243). Esse instantâneo contém um total de 13645 cadeias de blocos, aparentemente geradas pelo compilador Solidity, das quais apenas 1618 (11,8%) incluíram o comando send .
Destes, a grande maioria parece não usar nenhuma das técnicas defensivas de programação.
E o problema da corrida recursiva no TheDAO? O contrato inteligente mais emocionante atualmente, TheDAO [11] , sofre de um erro completamente separado, que é o fato de não ser "seguro para reutilização" [13] . Esse é outro tipo de programação insegura (conectada, mas distinta), que também era esperada em verificações de segurança anteriores [6] , mas, como antes, é provável que muitos contratos não sejam seguros atualmente. O trabalho futuro foi criar uma ferramenta que também pudesse detectar esse erro.
Onde tudo deu errado?
Não esperamos que a programação de contratos inteligentes seja completamente simples, pelo menos por enquanto. No entanto, é surpreendente que essa forma específica de erro seja tão difundida, apesar do fato de ter sido descrita há muito tempo durante o desenvolvimento do ecossistema Ethereum.
Um relatório de 2015 [6] fez essa recomendação aos desenvolvedores do Ethereum: "
Atualmente, os exemplos de programação apresentados na documentação são insuficientes para disseminar as melhores práticas para escrever contratos seguros e resolver o problema do mecanismo de gás. Os tutoriais introdutórios do C ++ geralmente ignoram
verificação de erros quanto à legibilidade, o que levou a vários erros de segurança. Os exemplos do Ethereum devem ensinar os melhores hábitos. Recomendação: forneça ainda mais exemplos da programação cuidadosa dos contratos de segurança ".
Conhecemos apenas uma resposta oficial para essa pergunta, que é adicionar um aviso à documentação oficial do Solidity mencionada anteriormente [3], repetida abaixo: "Existe algum perigo ao usar o envio : A transmissão falha se a profundidade da pilha de chamadas for 1024 (isso sempre pode ser chamado pelo chamador) e também falha se o destinatário ficar sem gás, para garantir uma transmissão segura, sempre verifique o valor de retorno do envio ou melhor: use o padrão em que o destinatário retira dinheiro ".
Acreditamos que essa observação não é suficiente para documentar o problema. Ele oferece apenas mitigação incompleta e descreve apenas uma versão do perigo, potencialmente enganando o leitor sobre seu grau.
Além disso, o aviso parece frequentemente ignorado. Portanto, acreditamos que são necessárias medidas preventivas adicionais.
Como a Etherscrape pode ajudar?
Acreditamos que o uso de ferramentas de análise estática, mesmo as cruas, como as descritas neste post, pode ajudar a melhorar a qualidade dos contratos inteligentes.No Etherscrape, integramos ferramentas de análise como essa em nosso serviço público da Web e adicionamos um link à página da ferramenta quando ela estiver pronta. Isso facilitará a exibição do código do contrato inteligente, destacando os locais onde podem ocorrer erros. Assumimos que os usuários de um contrato tão inteligente (por exemplo, potenciais investidores no TheDAO ou em suas ofertas) possam usar facilmente ferramentas como verificação de sanidade antes de depositar seu dinheiro. Até investidores não técnicos podem responsabilizar os desenvolvedores por explicar como reagiram aos problemas observados no código.
O Etherscrape também ajuda na análise da cadeia de blocos pública e no controle da prevalência desse erro, o que pode ajudar a decidir, por exemplo, quanto dinheiro deve ser alocado para pesquisa e desenvolvimento de ferramentas de análise estática. Além disso, compiladores como o solc podem integrar essas análises, fornecendo um aviso ao programador quando um erro parecer provável.
Leitura Recomendada