本文作者分享了以太坊虚拟机(EVM)的局限性以及在进行智能合约安全审计时的一些技巧,内容涵盖合约bytecode大小限制、gas优化、setter检查、循环检查、时间戳/区块依赖以及汇编检查等方面,旨在帮助开发者编写更安全、更高效的智能合约,并避免潜在的安全漏洞。
本文将只关注那些在保持代码安全方面可能非常有价值的领域。
合约字节码大小限制 - 24kb;
自定义错误 比 revert(“error text”) 占用更少的字节码空间;
包括旧的编码器 pragma abicoder v1;
如果合约中有数组的数组或结构的数组,则需要 Abicoder V2。但即使对于简单的参数,它也会产生更复杂的字节码 - 请记住这一点;
总的来说:public->external, memory->calldata;
可以删除或合并 Getters (包括 public 变量)以减少合约的大小;
你也可以拒绝短类型(只保留在存储中,在局部变量、计算和参数中使用完整类型);
可以不经常使用修饰器来减少合约大小;
为了减少字节码,你可以使 Optimizer runs=1
。 但是,不幸的是,它会使调用更昂贵;
{} - 用于限制局部变量可见性的块;
通常有访问控制;
通常必须发出事件;
必须更新存储:函数参数被写入存储(见过所有三种不正确的变体 - 它们很受欢迎🙂);
在复杂的项目中,我们会考虑更改其中一个系统组件的地址将如何影响整个协议;
检查它们不是无限的,也不会变成无限的(或者可以使用有限的变体);
从末尾开始的循环进行有效的检查,而不是 uint i = N; i >= 0
(因为 uint 类型总是返回一个正值);
在 Solidity 0.5.0 之前,do-while 内部的 continue 无法正常工作:continue 将执行切换到 do 而不检查 while 中的条件 - 这使得 do while 循环无限;
不要以块为单位测量时间(除非我们谈论的是硬分叉 - 它们与块号相关)。有时,由于 TimeBomb,块的速度会减慢,而切换到 PoS 会将块之间的时间固定为 12 秒,而不是目前 PoW 的平均 13 秒;
有时,合约依赖于非常小的时间间隔。一般来说,如果合约需要的精度高于 15 分钟,则可能存在危险;
有些合约检查某些事情发生的频率不超过每个块一次。作为一种优化(例如,收取利息)这是可以的,但作为防止任何重入\闪电贷\其他攻击的保护措施则不可行。它还会破坏集成 - 很多用户将通过一个合约进入。
a) “搞乱”第三个或第四个插槽; b) “损坏”已分配的内存 - 从第四个 slot 一直到空闲内存指针(第三个 slot 中的值); c) 经常因为将 returndata 复制到内存的开头而损坏,returndatacopy(0, 0, returndatasize()) —
;
如果汇编块是内存安全的应该被标记为“memory-safe”(如果它不遵循内存约定,则不标记);
汇编中没有溢出/下溢检查;
当从确切的存储槽写入/读取时,请在继承时考虑存储结构。 如果 B 继承自 A,则在使用 B 内部的 sstore() 时,必须小心不要意外覆盖 A 的基本合约的槽;
不同 Solidity 版本的设计变更:自 0.5.0 版本起使用 selfdestruct 代替 suicide,自 0.5.0 版本起使用 keccak256 代替 sha3。
最初发布在这里。
- 原文链接: x.com/officer_cia/status...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!