以太坊智能合约安全建议和最佳实践

  • guylando
  • 发布于 2022-01-17 18:58
  • 阅读 32

本文档提供了以太坊智能合约的安全建议和最佳实践,涉及ERC20代币标准、EVM特性、重入攻击、算术溢出、自毁函数、调用函数、gas限制、编译版本、合约部署、代码规范、代码审计等多方面的安全问题,并针对这些问题提供了相应的解决方案和防范措施。同时,本文档还列举了一些常用的安全工具,帮助开发者进行智能合约的安全分析和测试。

  1. 在 ERC20 中,先将 approve 更改为 0,然后再更改为目标值

    1. 如果不是更改为 0,则 tokens 应该在 approve 中验证之前是否为 0,就像在 minime 中一样,并且还要添加 increaseAllowance/decreaseAllowance,优先于 approve
    2. https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit
    3. https://github.com/ethereum/EIPs/issues/738
    4. https://github.com/ethereum/EIPs/pull/610#issuecomment-304746812 据说没有必要:https://github.com/Giveth/minime/pull/18#issuecomment-337928059
    5. minime 方案是最短和最好的,这里有一个解释为什么它足够:https://github.com/ethereum/EIPs/issues/20#issuecomment-277542427
  2. 在 constant 函数中使用诸如 block.coinbase, block.difficulty, block.gaslimit, block.number, block.timestamp, tx.gasprice, or tx.origin 之类的东西不是一个好主意,因为 EVM 将返回什么是不确定的,并且不同的实现,甚至同一实现的不同版本,可能会表现不同

    1. https://github.com/ethereum/EIPs/pull/610#issuecomment-327514172
  3. 重入:转移以太币会触发另一个合约,该合约触发返回当前合约,导致资金耗尽。这可以通过 Checks-Effects-Interactions 模式来解决 https://solidity.readthedocs.io/en/v0.5.8/security-considerations.html#re-entrancy

    1. 这导致了 DAO hack https://ethereum.stackexchange.com/questions/6210/how-was-the-recursion-created-that-lead-to-thedao-hack
    2. https://medium.com/spankchain/we-got-spanked-what-we-know-so-far-d5ed3a0f38fe
    3. 智能合约的执行是单线程的(但是交易之间的顺序是未知的,直到它们以足够的确认完成),这使得可以使用状态变量作为互斥锁,但是这会增加 gas 成本,因此其他解决方案可能更可取
      1. https://ethereum.stackexchange.com/questions/8261/how-to-solve-solidity-asynchronous-problem/8265#8265
      2. https://github.com/sigp/solidity-security-blog#preventative-techniques
      3. https://github.com/ethereum/wiki/wiki/Safety#pitfalls-in-race-condition-solutions
  4. 总是先做检查,然后是状态更改,然后才是外部合约调用或转移 https://solidity.readthedocs.io/en/v0.5.8/security-considerations.html#use-the-checks-effects-interactions-pattern

  5. 使用 "transfer" 可能会导致问题,最好允许提款。 始终使用 ERC20 approve+transferFrom 代替 transfer,这将有助于防止转移到错误的地址和其他错误,请参阅:https://solidity.readthedocs.io/en/v0.5.8/common-patterns.html#withdrawal-pattern

  6. 永远不要信任交易 origin 作为身份验证(origin 始终是发起初始交易的 EOA,而 msg.sender 可以是一个合约,该合约由于原始交易而进行了内部交易)https://solidity.readthedocs.io/en/v0.5.8/security-considerations.html#tx-origin

  7. 使用 openZepplin safemath 来防止任何数学运算的数学溢出和下溢 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-101

    1. 有一个关于 evm 操作码的 EIP 可以防止溢出,因此请在可用时使用它们 https://eips.ethereum.org/EIPS/eip-1051
  8. 使用适当的 pragma 启用 SMTChecker 以允许形式验证 https://solidity.readthedocs.io/en/v0.5.8/layout-of-source-files.html#smt-checker https://solidity.readthedocs.io/en/v0.5.8/security-considerations.html#formal-verification

  9. 不要添加 kill/self destruct,否则可能会导致资金损失:https://github.com/parity-contracts/0x863df6bfa4/pull/2 https://github.com/paritytech/parity-ethereum/issues/6995

  10. https://medium.com/loom-network/how-to-secure-your-smart-contracts-6-solidity-vulnerabilities-and-how-to-avoid-them-part-2-730db0aa4834

    1. 链接中的第 4 点:使用合约地址调用 selfdestruct,会将以太币发送到该地址,而无需调用合约 fallback 函数,因此永远不要基于余额做出决定。
      1. 另一种方法是使用挖矿奖励的 coinbase 交易
      2. 另一种方法是在创建合约之前预先资助一个地址 https://github.com/sigp/solidity-security-blog#pre-sent-ether
      3. 另一种方法是如果构造函数是 payable,则在创建合约时转移资金
    2. 第 5 点 - 始终假设转移和外部调用(的实例)可以触发 revert
      1. 从合约实例调用方法会传播错误并允许获取返回值,而使用 .call(..) 调用不需要知道 abi,失败时仅返回 false 而不 revert,并且不允许获取真实的返回值 https://ethereum.stackexchange.com/questions/30383/difference-between-call-on-external-contract-address-function-and-creating-contr
      2. 更新:从 solidity 编译器版本 0.5 开始,函数 .call 确实允许获取返回值,请参阅:https://solidity.readthedocs.io/en/v0.5.8/050-breaking-changes.html#semantic-and-syntactic-changes
    3. 始终避免使用 now 和 block.blockhash 用于合约的业务逻辑,因为它们的结果是可预测的或可以被矿工操纵 https://www.reddit.com/r/ethereum/comments/483rr1/do_not_use_block_hash_as_source_of_randomness/
    4. 此处推荐列表中的其他要点已经涵盖了更多内容 https://medium.com/loom-network/how-to-secure-your-smart-contracts-6-solidity-vulnerabilities-and-how-to-avoid-them-part-1-c33048d4d17d
  11. 应避免使用 .call() 和 .call().value()(.value(..) 添加要发送的以太币,.gas(..) 添加 gas 限制)。它们都不限制 gas(不调用 .gas(..)),并且返回 true/false 而不是在失败时进行 revert,并且它们可能导致重入漏洞 https://ethereum.stackexchange.com/questions/43782/importance-of-call-value/43821#43821

  12. 使用 send,transfer,.call().value(..).gas(..)()() 的用例。 基本上,大多数情况下使用 transfer,如果使用 call,则始终设置 .gas(..) 否则另一个合约可以通过执行 assert 来进行 DOS,该 assert 会占用所有 gas,从而阻止代码在调用后继续执行 https://ethereum.stackexchange.com/questions/19341/address-send-vs-address-transfer-best-practice-usage/38642#38642 https://github.com/ethereum/solidity/issues/610

  13. 总是更喜欢 throw 和 revert 而不是返回 true/false(对重入攻击更安全),如这里的讨论所示 https://github.com/ethereum/solidity/issues/610

    1. 关于为什么最好返回 false 而不是 throw 的一些好的论据:https://github.com/ethereum/EIPs/pull/610#issuecomment-305770167
    2. 关于为什么最好抛出而不是返回 false 的一些好的论据:https://github.com/ethereum/EIPs/issues/20#issuecomment-300500880 https://github.com/ethereum/EIPs/issues/20#issuecomment-300746940
  14. 一般来说,触发异常比返回 false 更安全,因为异常会 revert 对状态的任何更改,而返回 false 会将 revert 的责任留给调用者(如果需要)https://ethereum.stackexchange.com/questions/15140/would-it-be-better-to-use-throw-instead-of-return-false/15147#15147

  15. 新的 solidity 编译器版本删除了 throw 关键字,现在它有 assert,require,revert

    1. 关于 assert 用例的讨论(在 2018 年底,open zepplelin 更改了 safemath 以使用 require 而不是 assert)https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1120
    2. require 和 revert 将剩余的 gas 返回给用户,而 assert 使用收到的所有 gas
    3. require 用于输入验证,而 assert 主要用于静态代码分析,以识别永远不应该发生的情况并创建编译时警告
      1. https://ethereum.stackexchange.com/questions/27812/why-using-assert-since-it-would-consume-all-gas/27824#27824
      2. https://ethereum.stackexchange.com/questions/15166/difference-between-require-and-assert-and-the-difference-between-revert-and-thro/50957#50957
    4. require 与 revert 似乎是同一件事的不同语法:https://github.com/ethereum/solidity/issues/6689
  16. 如果合约不需要接收以太币(例如没有购买期的 erc20 token),则不要实现 payable 函数,也不要实现 fallback 函数。不要创建不必要的攻击接口。更多代码 = 更多攻击接口。转移到合约将自动 revert:https://ethereum.stackexchange.com/questions/34160/why-do-we-use-revert-in-payable-function/34164#34164

  17. 仍然需要一种方法来取出以太币,即使在某些方法的情况下,fallback 函数未实现,仍然会使用 selfdestruct 或转移接收以太币

  18. 应该使错误消息尽可能短,同时仍然清晰,因为错误消息会增加合约部署的 gas(而不会增加执行 gas)https://ethereum.stackexchange.com/questions/66879/does-a-string-message-increase-the-gas-usage-of-a-require-statement

  19. 在除法时,请确保数字是偶数,否则它将向下舍入

  20. 谨防重放攻击和不同的假设 https://media.consensys.net/discovering-signature-verification-bugs-in-ethereum-smart-contracts-424a494c6585

    1. 例如,以前以太坊主网交易和其他链之间可能会发生重放,这在 eip 155 中得到了解决:https://eips.ethereum.org/EIPS/eip-155
  21. 仅实现必要的功能,并尝试以最短和最有效的方式执行此操作,以避免创建不必要的攻击面和代码中的错误空间

  22. 使用经过审计和测试的代码 https://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own

  23. 编写尽可能多的单元测试 https://consensys.github.io/smart-contract-best-practices/software_engineering/#contract-rollout

  24. 执行安全审计并实施安全层体系结构,以防止未知情况 https://dasp.co/#item-10

  25. 内联汇编是一种在低级别访问以太坊虚拟机的方法。 这绕过了 Solidity 的几个重要的安全功能和检查。 仅应将其用于需要的任务,并且仅在你有信心使用它的情况下使用 https://solidity.readthedocs.io/en/v0.5.8/assembly.html#inline-assembly

  26. 即使未实现 selfdestruct,仍然可以使用 delegatecall,因此库永远不应调用 delegatecall/callcode

  27. 小心 delegatecall,因为它保留了上下文并允许被调用合约修改原始合约的状态。 所以调用者受被调用合约的支配 https://ethereum.stackexchange.com/questions/11578/how-to-protect-against-delegatecall-in-a-hub-spoke-contract-model/11598#11598

  28. callcode 也允许被调用合约修改原始合约存储,就像 delegatecall 一样

    1. https://ethereum.stackexchange.com/questions/3667/difference-between-call-callcode-and-delegatecall
    2. 检测你的智能合约是否由 callcode/delegatecall 调用:https://ethereum.stackexchange.com/questions/69551/how-to-prevent-the-code-of-my-contract-being-used-in-a-callcode/69552
  29. 使用 delegatecall 调用以修改原始合约状态的库,不应存储其自身的状态。 因为它们的状态实际上指向使用 delegatecall 的调用合约的存储插槽。 对于库,请使用 "library" 关键字而不是 "contract" 关键字,以在编译时检测问题(确保库合约是无状态且不可自我销毁的)https://github.com/sigp/solidity-security-blog/blob/master/README.md#preventative-techniques-3

  30. 状态变量的顺序很重要! 从最小到最大的类型对它们进行排序,以便分配的存储空间最小,这将需要更少的 gas 来部署

    1. 映射和动态大小的数组不遵循正常的存储规则
    2. 当 byte32 转换为 byte16 时,它是从左侧获取的 (0x922aba368bee9844aefc4b47b1d58d2857781b382dc1ad896d512e19131d108f -> 0x922aba368bee9844aefc4b47b1d58d28) https://gist.github.com/kenjirai/d1b9c135117eb39d7891d658cbd6154c
    3. 当 uint32 转换为 uint16 时,则取最小的字节 0x12345678 -> 0x5678
  31. 合约的私有成员是可访问的(例如使用 web3.eth.getStorageAt),因此请勿在合约中存储任何私有信息。 zksnarks 和零知识证明以及私钥签名可以允许证明秘密而不泄露秘密

    1. getStorageAt 返回所需存储插槽的 32 字节内容,该存储插槽是一个 32 字节的存储单元
  32. 合约的常量变量未存储在存储中

  33. 即使接口声明函数是 view/pure,函数实现合约也可以在没有 view/pure 的情况下定义该函数并修改状态。 因此,不能仅根据接口声明来假定状态未被修改

  34. 在与更改状态无关时调用某些内容:STATICCALL 操作码确保被调用函数不修改状态,新的 solidity 编译器将此用于 view/pure 函数 https://eips.ethereum.org/EIPS/eip-214

  35. 小心 EXTCODESIZE 和 CODESIZE,因为它们在合约初始化期间的行为有所不同:合约地址是从合约创建信息计算得出的,并且代码初始化代码在代码与合约地址关联之前执行,因此“在初始化代码执行期间,地址上的 EXTCODESIZE 应返回零,即帐户代码的长度,而 CODESIZE 应返回初始化代码的长度”https://medium.com/coinmonks/ethernaut-lvl-14-gatekeeper-2-walkthrough-how-contracts-initialize-and-how-to-do-bitwise-ddac8ad4f0fd

    1. “简而言之,如果你尝试在合约构造之前或期间检查智能合约的代码大小,你将获得一个空值。 这是因为智能合约尚未创建,因此无法自知其自身的代码大小”
  36. 初始化时出现问题的合约将具有地址,但没有代码

  37. 结构体

    1. 结构体声明默认为存储,因此始终在函数内部临时结构体的初始化中使用“memory”关键字,以防止将其保存到存储中。 同样,由于此原因,最好避免对临时计算使用结构体
    2. 如果在函数中声明一个新的存储结构体,它将覆盖其他全局存储的变量(从第一个插槽开始)
    3. 将内存结构体直接保存到状态变量时,内存结构体会自动强制转换为存储
    4. 函数输入是内存而不是存储支持的
    5. 在将函数输入结构体分配给存储变量结构体时,不能隐式地将内存转换为存储
    6. 尽管你无法从函数返回结构体,但你可以创建具有结构体值的映射状态变量,并且将为其创建一个默认的 getter,该 getter 返回结构体
  38. 从 remix 调用合约时,最好始终用引号引起来大数字,以避免出现问题和截断

  39. 合约地址是从交易确定性计算得出的,因此如果可以发送合约创建交易,该交易创建资金所在的地址(或以这种方式在创建合约之前预先资助合约),则可以追回丢失的资金

    1. https://github.com/sigp/solidity-security-blog#keyless-ether
    2. 地址是从发送者地址和 nonce 计算得出的。 对于合约,nonce 从 1 开始(0 用于合约自我创建):address(keccak256(0xd6, 0x94, YOUR_ADDR, 0x01))。 可以通过增加公式中的 nonce 来计算下一个合约地址
    3. https://medium.com/coinmonks/ethernaut-lvl-18-recovery-walkthrough-how-to-retrieve-lost-contract-addresses-in-2-ways-aba54ab167d3
  40. 如果部署了许多合约,并且想要通过使合约更小来优化部署 gas,则可以直接部署合约字节码,这可以使合约操作码尽可能小,例如初始化为 12 个操作码,运行时为 10 个操作码,请参阅:https://medium.com/coinmonks/ethernaut-lvl-19-magicnumber-walkthrough-how-to-deploy-contracts-using-raw-assembly-opcodes-c50edb0f71a2

  41. 数组

    1. 数组声明默认为存储,因此始终在函数内部临时数组的初始化中使用“memory”关键字,以防止将其保存到存储中
    2. 动态数组是用空 [] 定义的
    3. 动态数组必须初始化为“storage”变量
    4. 可以调整动态存储数组的大小,但是无法调整内存数组或固定大小数组的大小
    5. 调用 solidity 方法时,你会传递数组大小,并且不会针对实际负载进行检查。 这允许传递大于实际可能生成的数组大小的数组,并且可能允许其他漏洞利用,因此在开发合约时需要考虑在内(数组长度不受 solidity 约束)
    6. 不应允许任意数组长度更改和数组中的索引访问,因为它可能允许使用数组索引器访问任何合约存储
    7. 可以通过更改数组长度属性来更改数组长度。 需要小心下溢
    8. https://ylv.io/ethernaut-alien-codex-solution/
    9. https://f3real.github.io/Ethernaut_wargame2022.html
  42. 请注意,map 和数组以及任何其他变量实际上并未分配在独立的位置,它们都一起位于合约的存储插槽中,并且是对该存储中某些位置的引用(使用特殊公式计算存储中的数组和 map 位置),因此,例如,如果该数组足够大,则可以使用 map 和数组访问同一存储

  43. 请注意,revert 会 revert 所有内容,包括 gas 消耗量 https://ethereum.stackexchange.com/questions/4085/is-it-a-good-practice-to-log-an-event-every-time-i-throw-in-solidity

  44. https://blog.zeppelin.solutions/onward-with-ethereum-smart-contract-security-97a827e47702

    1. 如果可能,请避免使用 var 声明变量,否则该类型将是尽可能小的类型(对于 i=1,该类型将为 uint8,最大为 255)
      1. 更新:从 solidity 编译器版本 0.5 开始,不允许使用 var https://solidity.readthedocs.io/en/v0.5.8/050-breaking-changes.html#variables
    2. 自定义事件的建议命名是它们应该以 "Log" 开头,以便于识别
  45. 将失败字符串添加到 require 和 revert 命令中,以方便查找错误和检测错误

  46. 短地址攻击:如果用户地址以一个或多个零结尾,则通过省略零,如果发送系统不检查地址长度并创建交易,则将填充零,并且将使交易值添加零,从而导致不希望的金额转移

    1. https://dasp.co/#item-9
    2. 使用 assert(msg.data.length == numOfParams * 32 + 4) 进行缓解会为与 token 一起使用的多重签名钱包创建错误
      1. https://github.com/Giveth/minime/issues/8#issuecomment-348762658
      2. https://vessenes.com/notice-about-fun-token-support-for-multisignature-wallets-2/
      3. funfair token 为过渡到新合约的 gas 付费 https://funfair.io/new-fun-token-contract/
    3. 使用 >= 代替 == 进行缓解也存在问题 https://blog.coinfabrik.com/smart-contract-short-address-attack-mitigation-failure/
    4. OpenZeppelin 由于它们引起的错误而从智能合约中删除了短地址攻击验证 https://github.com/OpenZeppelin/openzeppelin-solidity/issues/261
    5. 在 solidity 0.5.0 中已修复,因此无需在智能合约中执行任何操作,但是应该在我们的系统中的存款/取款接口中执行地址验证 https://github.com/ethereum/solidity/pull/4224
  47. 必须使用最新的 solidity 版本进行编译才能获得最新的安全修复,例如 solidity 0.5.0 中的短地址攻击修复 https://github.com/ethereum/solidity/pull/4224

    1. https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-102
  48. 无法在合约开发期间仅进行艰苦的工作和安全,然后在部署后放松,因为 solidity 和 evm 升级多次将新的漏洞引入到以前安全的代码中,因此需要始终保持最新并准备好响应并为此类情况计划风险管理。示例:https://medium.com/chainsecurity/constantinople-enables-new-reentrancy-attack-ace4088297d9

    1. 另一个示例是 geth 协议更改导致了一个错误和资金损失 https://steemit.com/cryptocurrency/@barrydutton/breaking-the-biggest-canadian-coin-exchange-quadrigacx-loses-67-000-usdeth-due-to-coding-error-funds-locked-in-an-executable
    2. 定期阅读的地方的示例是 solidity 发行说明,以查看错误修复和行为更改:https://github.com/ethereum/solidity/releases
  49. 不要将智能合约代码(例如传递 .gas(2301)) 基于占用特定 gas 量的操作,例如 transfer 占用 2300 gas,因为这些操作可能会随时更改,如此处讨论:https://ethereum-magicians.org/t/remediations-for-eip-1283-reentrancy-bug/2434

  50. CREATE2 操作码允许修改合约代码而不修改地址(重新部署到同一地址),这可能对某些事情有用,但被认为是黑客行为,因此该能力可能会在未来的以太坊升级中删除,并且可以通过在有效代码的位置重新部署恶意代码来用于攻击

    1. 资源
      1. https://medium.com/@jason.carver/defend-against-wild-magic-in-the-next-ethereum-upgrade-b008247839d2
      2. https://ethereum-magicians.org/t/potential-security-implications-of-create2-eip-1014/2614
      3. openzepplin zos 正在考虑使用它 https://github.com/zeppelinos/zos/issues/152
      4. 使用 CREATE2 创建可重新部署的变形合约的代码:https://github.com/0age/metamorphic
      5. https://blog.ricmoo.com/wisps-the-magical-world-of-create2-5c2177027604
      6. https://github.com/ricmoo/will-o-the-wisp
      7. https://github.com/Zoltu/deterministic-deployment-proxy
    2. 保护智能合约免受可重新部署的智能合约(CREATE2)攻击的方法
      1. 不要与使用 selfdestruct 或 CALLCODE 或 DELEGATECALL 的可破坏合约和库进行交互
      2. 验证合同是由 EOA 创建的,还是由 CREATE 创建的,并且父合同一直是由 EOA 创建的,还是由 CREATE 创建的,而不是由 CREATE2 创建的
  51. SWC(智能合约弱点分类)收集已知的智能合约弱点

    1. 资源
      1. https://ethereum-magicians.org/t/eip-1470-smart-contract-weakness-classification-swc/1532
      2. https://github.com/SmartContractSecurity/SWC-registry
      3. https://github.com/ethereum/EIPs/issues/1469
      4. https://smartcontractsecurity.github.io/SWC-registry/
    2. 此列表中先前未出现的几个弱点
      1. 应始终适当地设置函数和变量的可见性,以防止未经授权的访问 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-100
        1. 所有函数名称都应以小写 camelCase 格式(例如 sendCoin),所有事件名称都应以大写 CamelCase 格式(例如 CoinTransfer)。 输入变量应以以下划线为前缀的小写 camelCase 格式(例如 _offerId),而输出变量对于纯 getter(即常量)函数应为 _r,在表示成功或失败时应为 _success(始终为布尔值),对于执行操作但需要返回值作为标识符的方法,应为其他值(例如 _maxValue)。 使用 _address 来引用地址(如果是一般地址),否则如果存在更具体的描述(例如 _from、_to)https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs
        2. 更新:从 solidity 编译器版本 0.5 开始,编译器强制执行显式函数可见性声明 https://solidity.readthedocs.io/en/v0.5.8/050-breaking-changes.html#explicitness-requirements
      2. 在非库合约中,合约中的 solidity 编译器 pragma 应锁定到测试合约的特定版本,以避免由于在不同的编译器版本中编译而引入漏洞(支持多个编译器版本更适合于其他合约使用的库)https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-103
        1. https://github.com/0xjac/ERC777/issues/61#issuecomment-479983564
      3. 始终验证 .call 返回值 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-104 https://github.com/sigp/solidity-security-blog#9-unchecked-call-return-values-1
      4. 除非断言永远不应该发生的错误,否则永远不要使用 assert。 不要将其用于逻辑 revert 或验证,因为它会消耗发送给交易的所有 gas https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-110
      5. 修复所有可以修复的警告,并且不要使用不推荐使用的函数 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-111
      6. 避免竞争条件,并且不要信任矿工 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-114 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-116
        1. https://dasp.co/#item-7
        2. https://dasp.co/#item-8
        3. https://github.com/sigp/solidity-security-blog#10-race-conditions--front-running-1
        4. https://github.com/sigp/solidity-security-blog#12-block-timestamp-manipulation-1
      7. 不要假设签名是唯一的,并且不要哈希签名 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-117
      8. 避免隐藏变量 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-119
        1. 所有函数名称都应以小写 camelCase 格式(例如 sendCoin),所有事件名称都应以大写 CamelCase 格式(例如 CoinTransfer)。 输入变量应以以下划线为前缀的小写 camelCase 格式(例如 _offerId),而输出变量对于纯 getter(即常量)函数应为 _r,在表示成功或失败时应为 _success(始终为布尔值),对于执行操作但需要返回值作为标识符的方法,应为其他值(例如 _maxValue)。 使用 _address 来引用地址(如果是一般地址),否则如果存在更具体的描述(例如 _from、_to)https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs
      9. 在没有 oracle 的情况下,不要信任区块链随机性 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-1204
        1. https://dasp.co/#item-6
        2. https://www.reddit.com/r/ethereum/comments/74d3dc/smartbillions_lottery_contract_just_got_hacked/
      10. 在处理签名时,请确保防止重放攻击 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-121
      11. 不要“自带加密”https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-122 https://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own
      12. 在多重继承中,从更一般到更具体地继承(编译器将从右向左线性化继承)https://consensys.github.io/smart-contract-best-practices/recommendations/#multiple-inheritance-caution https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-125
      13. 小心或什至避免使用函数指针或让用户输入设置其值 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-127
      14. 避免动态长度循环或操作,因为如果它们随着时间的推移而增加,则合约在 gas 方面可能会变得不可用 https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-128 https://github.com/sigp/solidity-security-blog#the-vulnerability-10
        1. 以下审计中提出了动态长度循环漏洞和建议的解决方案 https://mainframe.com/Mainframe_Secondary_Audit.pdf
      15. 检查合约代码的 unicode 字符,以确保在信任你所看到的内容之前,它们都是 ascii 字符,因为从右到左的覆盖和类似的 unicode 字符可能会破坏代码(例如,没有此检查就无法信任 etherscan 验证的代码)https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-130
      16. 使用接口类型代替地址以实现类型安全(请注意,这仍然无法防止恶意的外部实现,因此永远不要信任外部调用并使用 validation-effects-calls 模式)https://consensys.github.io/smart-contract-best-practices/recommendations/#use-interface-type-instead-of-the-address-for-type-safety
  52. 使用 metamask 测试合约时,不要忘记将 metamask 更改为测试网络或在没有真实以太币的情况下使用 metamask,否则可能会错误地使用真实的以太币并将其丢失

  53. 请注意,对于修改交易块,状态变量的修改在足够多的确认 (12+) 之后是可变的 https://ethereum.stackexchange.com/questions/8261/how-to-solve-solidity-asynchronous-problem/8265#8265

    1. 因此,要确保之前的函数调用已确认,需要在之前的调用中保存区块高度,并将其与当前调用中的区块高度进行比较
    2. 当交易结算时,叔块不会修改状态,即使它们向矿工提供了挖矿奖励 https://ethereum.stackexchange.com/questions/57727/are-uncle-blocks-unnecessary-overhead-on-blockchain
  54. 考虑使 tokens 可暂停,以便在发生黑客攻击时采取缓解措施,例如 BEC token 的情况,暂停功能挽救了大量资金:https://medium.com/secbit-media/a-disastrous-vulnerability-found-in-smart-contracts-of-beautychain-bec-dbf24ddbc30e https://nvd.nist.gov/vuln/detail/CVE-2018-10299

    1. MFT 是另一个发现漏洞的合约,该漏洞通过可暂停性得以保存:https://blog.mainframe.com/important-mft-contract-redeployed-4f0b0bd8dc3b
    2. EOS、Tron、Icon、OmiseGo、Augur、Status、Aelf、Qash 和 Maker tokens 都是可暂停的 https://blog.cryptofin.io/what-we-learned-from-auditing-the-top-20-erc20-token-contracts-7526ef3b6fb1
    3. 尽管正在讨论安全 tokens,但以下链接中关于为什么要使 token 可暂停的论点也部分适用于实用 tokens:https://github.com/ethereum/EIPs/issues/1400#issuecomment-420490082
    4. 确保在使用新的 solidity 编译器与 ERC20 代币交互时,使用包装器围绕 ERC20 接口,以便能够与许多实现了没有返回值的 ERC20 的代币(包括 BNB 和 OMG)进行交互。可以为此目的使用 open zeppelin 的 SafeERC20 库。
    5. https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
    6. https://docs.openzeppelin.org/docs/token_erc20_safeerc20
    7. 一些交易所使用其他包装器https://github.com/ethereum/solidity/issues/4116#issuecomment-439824542
  55. “以固定汇率运行接受多种货币的销售是危险且不好的” https://vitalik.ca/general/2017/06/09/sales.html

  56. 注意用户控制其帐户/密钥的假设,因为许多帐户由交易所控制而非用户控制 https://github.com/ConsenSys/singulardtv-contracts/issues/59

  57. 应该像这样使用 checkInvariants 修饰符https://github.com/ethereum/wiki/wiki/Safety#assert-guards

  58. 部署时使用 solidity 优化以降低 gas

  59. 不得在 erc20 approve 中添加对 amount 不大于用户余额的检查,因为这会阻止无限 approval 和提前 approval

    1. https://github.com/sec-bit/awesome-buggy-erc20-tokens/blob/master/ERC20_token_issue_list.md#a19-approve-with-balance-verify
  60. 在与合约交互时,有时最好先执行 call,只有在成功后才发送交易 https://gist.github.com/ethers/2d8dfaaf7f7a2a9e4eaa

  61. 考虑添加验证,确保 transfer 不会 transfer 到合约地址,请参阅此处 validDestination 修饰符 的实现(或者如果不添加它,则添加一种方法让所有者提取合约代币余额):https://consensys.github.io/smart-contract-best-practices/tokens/

  62. 考虑在发布到 etherscan 的代码中添加注释,其中包含有关在合约中发现问题应联系谁的信息 https://consensys.github.io/smart-contract-best-practices/documentation_procedures/

  63. 注意最负的带符号整数的取反(可能的解决方案是在计算结束时检查某些不变量,而不仅仅是 -1)https://consensys.github.io/smart-contract-best-practices/recommendations/#beware-of-negation-of-the-most-negative-signed-integer

  64. 确保没有阴影内置函数 https://consensys.github.io/smart-contract-best-practices/recommendations/#be-aware-that-built-ins-can-be-shadowed

  65. 请注意 safemath 仅适用于 uint256,因此在使用没有 safemath 的其他类型时要小心

  66. 为足够罕见的或/和可以延迟的敏感操作添加减速带延迟(还要确保添加一种暂停/取消的方式,以防出现问题,这与 DAO 不可阻挡的减速带不同,后者毫无用处),这将允许时间来验证/检测/响应攻击 https://consensys.github.io/smart-contract-best-practices/software_engineering/#speed-bumps-delay-contract-actions

  67. 考虑从业务角度尽可能添加速率限制 https://consensys.github.io/smart-contract-best-practices/software_engineering/#rate-limiting

  68. 偏向于比“owner”更细粒度的角色。open zeppelin 现在正在这样做:

    1. https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1146
    2. https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1274
  69. 确保在外部调用中限制 gas 以防止 GasToken 铸造等攻击 https://cryptopotato.com/ethereums-malicious-gastoken-minting-could-result-in-a-disaster-for-crypto-exchanges/

  70. 如果合约被用户发送了 ether,建议禁用合约而不是使用自毁 https://solidity.readthedocs.io/en/v0.5.8/introduction-to-smart-contracts.html#deactivate-and-self-destruct

  71. 要安全地离线签署数据并将其传递给合约,请使用 eip 712(通过使用 web3.eth.personal.sign),并且不要忘记将防重放 nonce 添加到签署的数据中,并且不要忘记将合约地址添加到签名中,以防止重放到重新部署的合约(跨合约重放攻击

    1. https://github.com/ethereum/EIPs/pull/712
    2. https://solidity.readthedocs.io/en/v0.5.8/solidity-by-example.html#what-to-sign
  72. 为了使合约对用户更清晰并避免一些误解,建议对代码中的任何 public/external 函数/变量使用 natspec,并且工具可以使用它来向最终用户显示

    1. https://solidity.readthedocs.io/en/v0.5.8/style-guide.html#natspec
    2. 它需要在编译后发布到 swarm(否则一个空格更改可能会使哈希值不同)
    3. json 元数据的哈希值嵌入在合约字节码后缀中 https://solidity.readthedocs.io/en/v0.5.8/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
    4. https://solidity.readthedocs.io/en/v0.5.8/natspec-format.html
    5. truffle 尚不支持 natspec:https://github.com/trufflesuite/truffle/issues/1118
    6. metamask 尚不支持 natspec:https://github.com/MetaMask/metamask-extension/issues/2501
    7. radspec 是 solidity 文档引用的 natspec 的实现 https://github.com/aragon/radspec#aside-why-is-natspec-unsafe
    8. 这里说很难找到 json 部署到的 swarm:https://blog.zeppelin.solutions/deconstructing-a-solidity-contract-part-vi-the-swarm-hash-70f069e22aef
    9. 支持状态变量的 natspec 已过时 1.5 年:https://github.com/ethereum/solidity/issues/3418
    10. 这里说 radspec 和 natspec 不兼容:https://github.com/aragon/radspec/issues/67
    11. 似乎 natspec 目前没有得到很好的支持,但未来可能会得到更好的支持
  73. 不要信任 msg.data 的哈希值,因为一些未使用的位可以用于更改 msg.data 的哈希值 https://solidity.readthedocs.io/en/v0.5.8/security-considerations.html#minor-details

  74. 当 SMTChecker 进入生产环境时启用它 https://solidity.readthedocs.io/en/v0.5.8/layout-of-source-files.html#smt-checker

  75. 如果你使用这些单位执行日历计算,请注意,并非每一年都等于 365 天,甚至并非每一天都有 24 小时,因为存在闰秒。由于闰秒无法预测,因此必须由外部预言机更新精确的日历库 https://en.wikipedia.org/wiki/Leap_second

  76. 使用 blockhash 时要小心。由于可扩展性原因,并非所有区块都提供区块哈希值。你只能访问最近 256 个区块的哈希值,所有其他值都将为零

  77. 请注意,打包的 abi 编码可能存在歧义,并且可能发生哈希冲突 https://solidity.readthedocs.io/en/v0.5.8/units-and-global-variables.html#abi-encoding-and-decoding-functions

  78. 如果你使用 ecrecover,请注意,可以在不需要知道相应私钥的情况下将有效签名转换为不同的有效签名 https://solidity.readthedocs.io/en/v0.5.8/units-and-global-variables.html#mathematical-and-cryptographic-functions

  79. ethereum 区块链将一些函数(sha256、ripemd160 或 ecrecover)实现为“预编译”合约,这些合约在首次使用后部署,因此首次使用会消耗更多 gas 并且可能耗尽 gas。仅与私有区块链相关 https://solidity.readthedocs.io/en/v0.5.8/units-and-global-variables.html#mathematical-and-cryptographic-functions

  80. 请注意,如果 C 是一个合约,那么 type(C).creationCode 和 type(C).runtimeCode 有很多陷阱,因此使用它们时要小心 https://solidity.readthedocs.io/en/v0.5.8/units-and-global-variables.html#type-information

  81. 你应该避免过度递归,因为每个函数调用至少占用一个堆栈槽,并且最多有 1024 个槽可用。对于外部调用(外部调用生成消息调用,而内部非前缀调用转换为跳转操作码),在消息调用中只能转发 63/64 的 gas,这实际上导致深度限制略低于 1000

  82. 低级函数 call、delegatecall 和 staticcall 如果被调用的帐户不存在,则返回 true 作为它们的第一个返回值,这是 EVM 设计的一部分。如果需要,必须在调用之前检查是否存在 https://solidity.readthedocs.io/en/v0.5.8/control-structures.html#error-handling-assert-require-revert-and-exceptions

  83. 在处理函数参数或内存值时,使用小于 256 位的类型没有内在的好处,因为编译器不会打包这些值 https://solidity.readthedocs.io/en/v0.5.8/miscellaneous.html#layout-of-state-variables-in-storage

  84. 对你希望被索引主题事件参数使用 "indexed" 关键字。每个事件最多允许 4 个索引参数。为了同时拥有值和快速搜索,可以将相同的值同时添加为索引参数和非索引参数 https://solidity.readthedocs.io/en/v0.5.8/abi-spec.html#events

  85. 如果合约使用包含已知错误的编译器编译,并且合约创建的时间是已经发布了包含修复程序的较新编译器版本的时间,那么这是非常可疑的(请注意,大多数错误并非关键,但应定期检查错误列表,尤其是在此底部的较新版本 https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json)https://solidity.readthedocs.io/en/v0.5.8/bugs.html

  86. 运算符 || 和 && 应用了常见的短路规则。这意味着在表达式 f(x) || g(y) 中,如果 f(x) 的计算结果为 true,则即使 g(y) 可能具有副作用,也不会对其进行计算

  87. 类型 byte[] 是字节数组,但由于填充规则,它为每个元素浪费 31 个字节的空间(存储中除外)。最好使用 bytes 类型代替。作为一般规则,对于任意长度的原始字节数据使用 bytes,对于任意长度的字符串 (UTF-8) 数据使用 string。如果可以将长度限制为一定数量的字节,则始终使用值类型 bytes1 到 bytes32 之一,因为它们更便宜 https://solidity.readthedocs.io/en/v0.5.8/types.html#fixed-size-byte-arrays https://solidity.readthedocs.io/en/v0.5.8/types.html#bytes-and-strings-as-arrays

  88. 确保硬编码的文字地址通过校验和测试,否则它们将被视为常规有理数 https://solidity.readthedocs.io/en/v0.5.8/types.html#address-literals

  89. 下划线应该用于分隔数字文字的数字,以帮助提高可读性并防止错误,它们不会影响数字 https://solidity.readthedocs.io/en/v0.5.8/types.html#rational-and-integer-literals

  90. 请注意,“string”类型是 UTF8 编码的,因此通过 "bytes(s)[7] = 'x';" 请记住,你正在访问 UTF-8 表示的低级字节,而不是单个字符 https://solidity.readthedocs.io/en/v0.5.8/types.html#bytes-and-strings-as-arrays

  91. 请注意,delete 对映射没有影响 https://solidity.readthedocs.io/en/v0.5.8/types.html#delete

  92. 显式的溢出转换负整数是可能的,因此要小心 https://solidity.readthedocs.io/en/v0.5.8/types.html#explicit-conversions

  93. 始终重新检查事件数据,不要仅仅依赖于基于索引参数的搜索结果,因为如果结构体包含多个动态大小的数组,则结构体的编码是模棱两可的 https://solidity.readthedocs.io/en/v0.5.8/abi-spec.html#encoding-of-indexed-event-parameters

  94. 可读且一致的代码将使在开发过程中更容易检测到错误,并最终导致更安全的代码,因此请遵循此处的约定:https://solidity.readthedocs.io/en/v0.5.8/style-guide.html

  95. 视图函数没有防止状态修改的运行时检查 https://solidity.readthedocs.io/en/v0.5.8/contracts.html#view-functions

  96. 状态读取不由 evm 强制执行 https://solidity.readthedocs.io/en/v0.5.8/contracts.html#pure-functions

  97. 事件的匿名关键字使事件名称不被存储,这使得合约 gas 部署成本更低,调用事件的成本也更低。当合约只有一个事件时,应使用该关键字,其中事件名称不受某些标准强制执行,因为那时所有日志都知道来自此事件,并且无需存储事件名称 https://github.com/ethereum/solidity/pull/6791#issuecomment-493989944

  98. 对于代币合约(以及一些其他敏感合约),除了可暂停之外,还应该有一个升级/迁移策略,如此处所述:https://github.com/ethereum/EIPs/issues/644#issuecomment-494106553

    1. 在此处查看 SafeUpgradeableTokenERC20 合约实现:https://github.com/guylando/EthereumSmartContracts
  99. 在可能/有用的地方添加事件,以便更容易对已部署的合约进行审计、调试和发现不当行为

  100. 在代币转账函数中,通过添加类似 require( _to != address(this) ) 的代码来防止错误地转移到代币地址,请参阅此处的第 3.1 节:https://github.com/EthereumCommonwealth/Auditing/issues/236

  101. 在代币 decreaseAllowance 函数中,如果大于余额则设置为零,而不是恢复,这样例如当有人想使用 decreaseAllowance 快速从攻击者那里删除 allowance 时,阻止恢复的发生,这会给攻击者更多的时间来使用他的 allowance,请参阅此处的第 3.2 节:https://github.com/EthereumCommonwealth/Auditing/issues/236

  102. 为了熟悉可能的漏洞并获得想法,请查看 https://github.com/EthereumCommonwealth/Auditing/issues?q=is%3Aissue+is%3Aclosed 中的先前报告,并在完成合约开发后考虑将其提交给 https://github.com/EthereumCommonwealth/Auditing 进行审计

  103. 当将 SafeERC20 与具有非恢复回退函数的非兼容代币一起使用时要小心,因为 SafeERC20 函数(如 safeTransferFrom)不会提示代币中未实现 transferFrom,而是会静默调用回退函数,请参阅:https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1769

  104. 在调用其他合约并期望它们在出现问题时失败时要小心,因为如果其他合约未实现被调用的函数并实现非恢复回退函数,即使未调用所需的函数,交易也会成功

  105. 在向用户提供公共 burn 函数以 burn 他们的代币时要小心,因为发送到不可恢复的地址和 burn (减少总供应量)之间存在差异,因为前者对非技术投资者不可见,而后者会更改 coinmarketcap 上的代币总供应量,这可能会引起关注 coinmarketcap 和类似工具中代币的非技术投资者的不希望的反应。另一个区别是,关于检索锁定代币的讨论更多是指发送到无效地址的代币,而不是 burn 的代币,这表明将代币发送到无效地址可能仍然在法律上使这些代币属于其所有者,请参阅此处的讨论:https://github.com/ethereum/EIPs/pull/867#issuecomment-365746101

  106. 使用修饰符使代码更简洁易懂(修饰符是在行内编译的)。更简洁和更易读的代码提高了更早地发现错误并在更好和更安全的代码中的机会。

  107. 用于扫描智能合约和开发智能合约的安全工具和其他有用工具(首先检查扫描器是否对合约代码具有分发权):

    1. https://github.com/ConsenSys/mythril-classic
      1. 在 windows 上运行
        1. 首先安装 docker desktop 应用
        2. 如果合约使用 solidity 编译器 0.4.24 并且合约名称为 DDD,并且位于 C:\Users\user\Downloads\target.sol 中,则运行 "docker run --rm -v C:\Users\user\Downloads:/Downloads mythril/myth -x /Downloads/target.sol:DDD -mether_thief --verbose-report --solv 0.4.24"
        3. -v 命令将主机中 ':' 左侧的文件夹映射到 docker 中 ':' 右侧的文件夹
        4. --rm 命令在完成运行命令后清除 docker,因此如果想运行几个相关的/依赖的命令,则省略 -rm 并在最后添加它以进行清理 https://docs.docker.com/engine/reference/run/#clean-up---rm
      2. 注意:运行后,需要等待约 10-20 分钟才能完成(在此期间它不会显示任何输出,因此请耐心等待),因此更好的主意是不等待它并在运行时做其他事情
      3. 结论:是的。它很好,应该始终使用它来获得现有漏洞的一般方向(它不会产生确切的漏洞利用,但如果发现漏洞,会给出方向)
    2. http://remix.ethereum.org
      1. 在 remix 中编译执行静态分析,可能会检测到错误
      2. 结论:是的。应该使用它,但即使不是全部,大多数警告都是误报,因此不要在它们上面浪费太多时间
    3. https://github.com/crytic/slither
      1. 需要 python 3.6
      2. pip install slither-analyzer
      3. 下载最新的 solc 编译器版本并将其放在 PATH 中(否则 slither 将抛出“找不到文件”错误)https://github.com/ethereum/solidity/releases
      4. 重新打开 cmd 并运行 "slither contract_file_path_and_filename.sol"
      5. 结论:是的。它很好,应该始终使用它来检测小的可能的优化和修复
    4. https://github.com/crytic/eth-security-toolbox
      1. 非常重,需要很长时间才能下载
      2. 包含 slither、echidna、manticore 和其他一些工具
      3. docker pull trailofbits/eth-security-toolbox(需要 10 分钟以上)
      4. docker run -it -v C:\Users\user\Downloads:/Downloads trailofbits/eth-security-toolbox(将 "user" 替换为 windows 用户名)
      5. slither /Downloads/contract.sol(其中 contract.sol 位于 windows 用户下载文件夹中)
      6. echidna-test /Downloads/contract.sol ContractName
      7. 结论:是的。如果使用其中的许多工具,则 eth-security-toolbox 下载速度很慢,但很方便
    5. https://github.com/crytic/echidna
      1. 使用 eth-security-toolbox docker
      2. 需要编写测试才能使其工作,因此它不是即插即用的解决方案
      3. 结论:否。对于简单的快速安全检查来说不是很有用,因为它需要开发
    6. https://github.com/trailofbits/manticore
      1. 使用 eth-security-toolbox docker
      2. https://github.com/trailofbits/manticore/issues/1382
      3. https://github.com/trailofbits/manticore/issues/1315
      4. 结论:否。存在很多错误,导致它现在无法使用
    7. solidity security vscode 扩展 https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-visual-auditor
      1. 结论:在开发期间使用它来修复键入代码时出现的问题
    8. https://github.com/melonproject/oyente
      1. https://github.com/melonproject/oyente/issues/334
      2. https://github.com/melonproject/oyente/issues/268
      3. 结论:否。已过时、已弃用且不再维护
    9. https://github.com/raineorshine/solgraph
      1. 不支持新的 solidity 版本 https://github.com/raineorshine/solgraph/issues/39
      2. 结论:否。已过时、已弃用且不再维护
    10. https://github.com/MAIAN-tool/MAIAN
      1. 结论:否。已过时、已弃用且不再维护
    11. https://github.com/quoscient/octopus
      1. 结论:否。文档不完善且未积极开发
    12. https://github.com/cleanunicorn/karl
      1. 结论:否。未积极开发,并且在 windows 上的安装甚至不起作用。它只是在已部署的合约上运行 mithril,因此如果目标是分析你的合约,那么最好直接运行 mithril
    13. https://github.com/ConsenSys/surya
      1. npm install -g surya
      2. "surya describe contract.sol" 快速显示文件中函数和合约的摘要
      3. "surya graph contract.sol > dot -Tpng > contract.png" 创建合约调用图(dot 工具可在此处获得:https://graphviz.gitlab.io
      4. 图创建不起作用 https://github.com/ConsenSys/surya/issues/76
      5. 结论:是的。对于一般合约分析来说可能很好,可以帮助注意到问题,但不提供漏洞信息
    14. SECBIT Solidity 静态分析器
      1. https://github.com/sec-bit/adelaide/blob/secbit-ssae/README.secbit.md
      2. 可作为 vscode 扩展使用:https://github.com/sec-bit/vscode-secbit-ssae
      3. https://marketplace.visualstudio.com/items?itemName=SECBIT.vscode-secbit-ssae
      4. 结论:否。不起作用且未积极开发或维护
    15. https://github.com/b-mueller/scrooge-mcetherface
      1. 仅在测试环境中运行
      2. 没有 docker 并且使用 mithril,mithril 与 windows 不兼容,因此最简单的安装方法是安装了 docker(例如 eth-security-toolbox docker)的情况下运行:
        1. 如果容器正在运行
          1. "docker ps"
          2. 复制 ubuntu docker 实例的 CONTAINER ID
          3. "docker attach {container-id}"
        2. 如果容器未运行(或者未作为 root 运行,则 "exit" 并运行此命令以作为 root 启动)
          1. docker run -u 0 -v C:\Users\user\Downloads:/Downloads -it trailofbits/eth-security-toolbox
        3. 然后在该 shell 中运行安装步骤 https://github.com/b-mueller/scrooge-mcetherface#installation
      3. 结论:否。未积极开发或维护或支持,并且在 windows 上的安装不起作用。它只是在已部署的合约上运行 mithril,因此如果目标是分析你的合约,那么最好直接运行 mithril
    16. https://github.com/protofire/solhint
      1. npm install -g solhint
      2. solhint init-config
      3. solhint contract.sol
      4. 结论:是的。有助于获得格式提示,但不提供安全提示
    17. https://github.com/duaraghav8/Ethlint
      1. npm install -g ethlint
      2. solium --init
      3. solium -f contract.sol
      4. 结论:是的。有助于获得有关格式、编码和安全的建议
    18. https://github.com/smartdec/smartcheck
      1. npm install @smartdec/smartcheck -g
      2. smartcheck -p contract.sol
      3. 可以使用 https://tool.smartdec.net/ 自动运行
      4. 结论:是的。给出 90%-100% 误报的安全建议,因此使用它但重新验证这些建议。不要使用命令行工具,因为它不清楚,只使用在线界面。
    19. https://github.com/eth-sri/securify https://securify.chainsecurity.com/
      1. 结论:否。不起作用。正常安装和 docker 安装均失败,并且在尝试使用他们的网站扫描粘贴的代码或 github 存储库时,它卡在无限 404 XHR 错误中,直到最终显示 "bad gateway" 错误
    20. https://github.com/tagomaru/truffle-sca2t
      1. 基本上只集成了使用 truffle run mythx 命令运行 mythx
      2. 结论:否。应该更喜欢使用 mythx 官方集成(truffle-security npm ),该集成受到官方支持并且得到更积极的开发
    21. https://github.com/rajeevgopalakrishna/Solstice
      1. https://medium.com/@rajeevgopalakrishna/solstice-solidity-security-tool-for-investigative-contract-examination-1fafda26d038
      2. 结论:否。官方安装步骤给出语法错误,并且未积极开发或维护
    22. mythx + mythos + 官方 truffle 集成
      1. npm install -g truffle-security
      2. truffle run verify
      3. https://github.com/ConsenSys/truffle-security
      4. https://docs.mythx.io/en/latest/tooling/truffle.html
      5. https://medium.com/consensys-diligence/mythx-and-truffle-security-painless-smart-contract-security-testing-6d0fe5e938da
      6. https://github.com/cleanunicorn/mythos
      7. https://docs.mythx.io/en/latest/tooling/mythos.html
      8. 基本上 mythx 是一个服务器端 api,mythos 和 truffle-security 以及其他工具只是与 mythx api 集成的客户端工具,因此使用哪个工具无关紧要。
      9. 结论:是的。使用 truffle-security 没有产生任何输出,因此改用 mythos,并且它按预期执行了安全分析。应该始终使用它来获得安全分析提示
    23. https://github.com/cgewecke/eth-gas-reporter
      1. 如果 solidity-coverage 抛出有关找不到 eth-gas-reporter 的错误,请确保所有内容都安装在同一范围内(全局安装 eth-gas-reporter 可能会解决问题)
      2. 结论:是的。有助于在合约的单元测试中找到占用过多 gas 的有问题的功能
    24. https://github.com/sc-forks/solidity-coverage
      1. 查看 open zeppelin 存储库以获取添加到 truffle-config.json 的覆盖率,并查看 .solcover.js 文件内容,但在 .solcover.js 中仅使用 norpc:true 并将 skipFiles 更改为指向你的 truffle 迁移文件,并在其中添加你的模拟合约文件
      2. npm install --save-dev solidity-coverage
      3. ./node_modules/.bin/solidity-coverage
      4. node_modules.bin\solidity-coverage
      5. 默认情况下,对于 solidity 0.5.0+ 返回错误 https://github.com/sc-forks/solidity-coverage/issues/316
        1. open zeppelin 安装了 https://github.com/rotcivegaf/solidity-coverage fork 以支持 solidity 0.5.0+,因此可以做同样的事情
        2. 但最新的 solidity-coverage 开发发生在此分支上:https://github.com/sc-forks/solidity-coverage/tree/leapdao
        3. 可以使用以下命令安装活动分支:npm install --save-dev git://github.com/sc-forks/solidity-coverage#leapdao
      6. 按照那些用于在 windows 上运行的说明:https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-on-windows
        1. 在运行覆盖率之前,在另一个终端中运行 "node_modules.bin\testrpc-sc -p 8555"
      7. 如果收到 "Returned error: Exceeds block gas limit" 错误,则从 truffle-config.json 中的 coverage 配置中删除 gas、gasPrice 配置
      8. 如果收到 "invalid opcode" 错误,则最好不要运行 testrpc-sc,而是使用 ganache-cli 并将 truffle-config.json coverage 端口更改为 8454 ganache cli 端口 https://ethereum.stackexchange.com/questions/69866/solc-error-invalid-opcode-but-works-in-truffle
        1. 更新:使用 ganache-cli 会导致其他错误发生,并且不允许获得覆盖率报告,因此更好的解决方案是将 solidity-coverage 更新到活动分支,如上所述
      9. 如果收到 "base fee exceeds gas limit" 错误,则在 truffle-config.json 中增加或删除 coverage 网络配置中的 gas 配置
      10. 如果收到 "out of gas" 错误(因为合约的检测使其体积太大),则
        1. 将 "--allowUnlimitedContractSize" 参数添加到 testchain 执行中,以允许部署大于 24KB 的合约(否则即使原因是大小而不是 gas 也会出现 gas 不足错误 https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md
        2. 将 truffle-config.json 中 coverage 网络配置的 gas 参数增加到 0xfffffffffff(发送到区块链的内容)并将 ganache-cli gas 限制增加到 0xfffffffffff(默认约为 670 万),使用 -l 参数(区块链中支持的内容):“ganache-cli -l 0xfffffffffff”
      11. 创建非常有帮助的 instanbul html 报告,这些报告可以快速帮助添加缺失的测试用例
      12. 结论:是的。可能很难使其工作,但它是必须使用的工具,用于提高测试覆盖率,并且在所有配置都设置好后,它可以按预期工作
  • 原文链接: github.com/guylando/Know...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
guylando
guylando
江湖只有他的大名,没有他的介绍。