Diretrizes de segurança para contratos inteligentes
Siga estas recomendações de alto nível para construir contratos inteligentes mais seguros.
Diretrizes de design
O design do contrato deve ser discutido com antecedência, antes de escrever qualquer linha de código.
Documentação e especificações
A documentação pode ser escrita em diferentes níveis e deve ser atualizada durante a implementação dos contratos:
- Uma descrição do sistema em linguagem simples, descrevendo o que os contratos fazem e quaisquer suposições sobre a base de código.
- Esquemas e diagramas de arquitetura, incluindo as interações do contrato e a máquina de estado do sistema. Os printers do Slither (opens in a new tab) podem ajudar a gerar esses esquemas.
- Documentação minuciosa do código, o formato NatSpec (opens in a new tab) pode ser usado para a Solidity.
Computação onchain vs offchain
- Mantenha o máximo de código que puder offchain. Mantenha a camada onchain pequena. Faça o pré-processamento de dados com código offchain de tal forma que a verificação onchain seja simples. Você precisa de uma lista ordenada? Ordene a lista offchain e, em seguida, verifique apenas a sua ordem onchain.
Capacidade de atualização
Discutimos as diferentes soluções de capacidade de atualização em nossa postagem no blog (opens in a new tab). Faça uma escolha deliberada de suportar ou não a capacidade de atualização antes de escrever qualquer código. A decisão influenciará como você estrutura seu código. Em geral, recomendamos:
- Favorecer a migração de contrato (opens in a new tab) em vez da capacidade de atualização. Os sistemas de migração têm muitas das mesmas vantagens que os atualizáveis, sem as suas desvantagens.
- Usar o padrão de separação de dados em vez do delegatecallproxy. Se o seu projeto tiver uma separação clara de abstração, a capacidade de atualização usando a separação de dados exigirá apenas alguns ajustes. O delegatecallproxy requer experiência na EVM e é altamente propenso a erros.
- Documente o procedimento de migração/atualização antes da implantação. Se você tiver que reagir sob estresse sem nenhuma diretriz, cometerá erros. Escreva o procedimento a seguir com antecedência. Ele deve incluir:
- As chamadas que iniciam os novos contratos
- Onde as chaves estão armazenadas e como acessá-las
- Como verificar a implantação! Desenvolva e teste um script pós-implantação.
Diretrizes de implementação
Busque a simplicidade. Use sempre a solução mais simples que atenda ao seu propósito. Qualquer membro da sua equipe deve ser capaz de entender a sua solução.
Composição de funções
A arquitetura da sua base de código deve tornar o seu código fácil de revisar. Evite escolhas arquitetônicas que diminuam a capacidade de raciocinar sobre a sua exatidão.
- Divida a lógica do seu sistema, seja por meio de vários contratos ou agrupando funções semelhantes (por exemplo, autenticação, aritmética, ...).
- Escreva funções pequenas, com um propósito claro. Isso facilitará a revisão e permitirá o teste de componentes individuais.
Herança
- Mantenha a herança gerenciável. A herança deve ser usada para dividir a lógica, no entanto, seu projeto deve ter como objetivo minimizar a profundidade e a largura da árvore de herança.
- Use o printer de herança (opens in a new tab) do Slither para verificar a hierarquia dos contratos. O printer de herança ajudará você a revisar o tamanho da hierarquia.
Eventos
- Faça o log de todas as operações cruciais. Os eventos ajudarão a depurar o contrato durante o desenvolvimento e a monitorá-lo após a implantação.
Evite armadilhas conhecidas
- Esteja ciente dos problemas de segurança mais comuns. Existem muitos recursos online para aprender sobre problemas comuns, como Ethernaut CTF (opens in a new tab), Capture the Ether (opens in a new tab) ou Not so smart contracts (opens in a new tab).
- Esteja ciente das seções de avisos na documentação da Solidity (opens in a new tab). As seções de avisos informarão você sobre comportamentos não óbvios da linguagem.
Dependências
- Use bibliotecas bem testadas. Importar código de bibliotecas bem testadas reduzirá a probabilidade de você escrever código com bugs. Se você quiser escrever um contrato ERC-20, use a OpenZeppelin (opens in a new tab).
- Use um gerenciador de dependências; evite copiar e colar código. Se você depende de uma fonte externa, deve mantê-la atualizada com a fonte original.
Teste e verificação
- Escreva testes de unidade minuciosos. Um conjunto de testes abrangente é crucial para construir software de alta qualidade.
- Escreva verificações e propriedades personalizadas para o Slither (opens in a new tab), o Echidna (opens in a new tab) e o Manticore (opens in a new tab). Ferramentas automatizadas ajudarão a garantir que seu contrato seja seguro. Revise o restante deste guia para aprender como escrever verificações e propriedades eficientes.
- Use o crytic.io (opens in a new tab). O Crytic se integra ao GitHub, fornece acesso a detectores privados do Slither e executa verificações de propriedades personalizadas do Echidna.
Solidity
- Favoreça a Solidity 0.5 em vez da 0.4 e 0.6. Em nossa opinião, a Solidity 0.5 é mais segura e tem melhores práticas integradas do que a 0.4. A Solidity 0.6 provou ser muito instável para produção e precisa de tempo para amadurecer.
- Use uma versão estável para compilar; use a versão mais recente para verificar se há avisos. Verifique se o seu código não tem problemas relatados com a versão mais recente do compilador. No entanto, a Solidity tem um ciclo de lançamento rápido e um histórico de bugs no compilador, portanto, não recomendamos a versão mais recente para implantação (veja a recomendação de versão do solc (opens in a new tab) do Slither).
- Não use assembly inline. O assembly requer experiência na EVM. Não escreva código EVM se você não tiver dominado o yellow paper.
Diretrizes de implantação
Uma vez que o contrato tenha sido desenvolvido e implantado:
- Monitore seus contratos. Observe os logs e esteja pronto para reagir em caso de comprometimento do contrato ou da carteira.
- Adicione suas informações de contato ao blockchain-security-contacts (opens in a new tab). Esta lista ajuda terceiros a entrarem em contato com você caso uma falha de segurança seja descoberta.
- Proteja as carteiras de usuários privilegiados. Siga nossas melhores práticas (opens in a new tab) se você armazenar chaves em carteiras de hardware.
- Tenha um plano de resposta a incidentes. Considere que seus contratos inteligentes podem ser comprometidos. Mesmo que seus contratos estejam livres de bugs, um invasor pode assumir o controle das chaves do proprietário do contrato.