转载请微信联系:huangdiezi,更多DAO、Web3、NFT、Metaverse资讯请关注老雅痞👇
来源Georgios Konstantopoulos
亲爱的朋友们,大家好,我是公众号老雅痞的小编波动,雅痞哥为了敦促我学习区块链/DAO/NFT知识,我们将单独开始一个专栏,从零开始学习。每天在公众号FastDaily和老雅痞各更新一篇。
让我们每天学点新东西,争取不白活。
欢迎大家和我一起学习进步(微信yaoyaobigc)~揪咪~

在之前的学习中,我们了解了以太坊可扩展性的未来。现在假设所有这些可扩展性问题现在都已解决,并且以太坊的智能合约可以正常工作。
这些用户是善意的,还是他们可能是干扰合约顺利运行的对手?
智能合约是“不可变的”。一旦部署它们,它们的代码就无法更改,因此无法修复任何已发现的错误。
在一个潜在的未来整个组织都由智能合约代码管理, 非常需要适当的安全。过去的黑客攻击,例如TheDAO 或今年的 Parity hack (七月,十一月) 提高了开发者的意识,但这远远不够。
“这是黑客的迪士尼乐园”
在本文中,我们将介绍一些著名的安全陷阱及其缓解措施。
1. 上溢和下溢
当一个数字增加超过其最大值时就是溢出。举个例子,Solidity 最多可以处理 256 位数字(最多2²⁵⁶-1),因此增加 1 将导致 0。
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ 0x000000000000000000000000000000000001
----------------------------------------
= 0x000000000000000000000000000000000000

达到最大读数后,里程表或行程表从零重新启动,称为里程表翻转。
同样,在相反的情况下,当数字为无符号 unsigned 时,递减将使数字下溢,从而产生最大可能值。
0x0000000000000000000000000000000000000
- 0x000000000000000000000000000000000001
----------------------------------------
= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

尽管这两种情况都很危险,但下溢情况更有可能发生,例如在代币持有者拥有X个代币但试图花费 X+1的情况下。如果代码没有检查,攻击者最终可能会被允许超额花费并拥有一个最大化的余额。
缓解措施:一段时间以来,使用 OpenZeppelin 的SafeMath 库已成为标准做法。


2. 可见性和委托调用
如果在 7 月份你关注过类似问题的人,这个 bug 应该很熟悉,毕竟这是 Parity 钱包黑客事件,让用户损失了大约 3000 万美元。
Solidity 可见性修饰符及其区别。
任何人都可以调用公共函数(通过合约内部的函数、继承合约的函数或外部用户)
外部函数只能被外部访问,这意味着它们不能被合约的其他函数调用。下面的要点没有编译,外部可见性cannotBeCalled不允许它被合约的函数调用(但是它可以被另一个合约调用)
External 使用起来更便宜,因为它使用calldata操作码,而 public 需要将所有参数复制到内存。

Private和internal更简单:private意味着该函数只能从合约内部调用,同时internal证明了更宽松的限制,允许从父合约继承的合约使用该函数。
也就是说,保留你的功能private ,除非internal 需要外部交互。
代表调用Delegatecall
从solidity docs解释:
“Delegatecall 与消息调用相同,区别在于,目标地址的代码在调用合约的上下文中执行msg.sender并且msg.value不会更改它们的值。
这意味着合约可以在运行时从不同的地址动态加载代码。存储、当前地址和余额仍然是调用合约,只是代码取自被调用地址。”
这个低级函数非常有用,它是实现库和模块化代码的支柱。然而,它打开了漏洞的大门。
在下面的示例中,攻击者可以调用合约 Delegate 的公共函数pwn,并且由于调用是在上下文中的Delegation,他们可以声明合约的所有权。
Parity hack 涉及不安全的可见性修饰符和滥用delegate调用与 abritrary 数据的组合。易受攻击的合约的功能已实现delegatecall,另一个合约中可以修改所有权的功能被公开。这使得攻击者可以制作该msg.data字段来调用易受攻击的函数。
至于msg.data字段中包含的内容,那就是您要调用的函数的签名。sha3 (alias for keccak256)这里的签名是指函数原型散列的前 8 个字节。

在这种情况下:
web3.sha3("pwn()").slice(0, 10) --> 0xdd365b8b
如果函数接受参数,pwn(uint256 x):
web3.sha3("pwn(uint256)")。切片(0,10)-> 0x35f4581b
3. 重入(TheDAO hack)
Solidity 的call 函数在调用时value转发它收到的所有gas。在下面的代码段中,在实际减少发件人的余额之前,调用就已经进行了。这打开了一个漏洞,当 TheDAO hack 发生时,reddit评论中对此进行了很好的描述:
“简单来说,这就像银行出纳员在给你所有你要求的钱之前不会改变你的余额。“我可以提取 500 美元吗?等等,在那之前,我可以提取 500 美元吗?”
等等。设计的智能合约只在开始时检查你有没有 500 美元,就检查一次,还允许被打断。”
解决方法是在进行价值转移之前减少发件人的余额。对于使用过并行编程的人来说,另一种解决方案是使用mutexes。
目前,使用msg.sender.transfer(_value) 是最佳实践。如果你真的需要使用send使用require(msg.sender.send(_value));
第 一 部分到此结束。第二部分将更新在老雅痞公众号上,我们将讨论一些鲜为人知的漏洞利用、应该添加到工作流程中的工具以及智能合约安全的未来。
往期学习回顾:
「DAO」剪刀标签Scissor Labels:关于叙述争夺、激励以及如何造成分裂
NFT词典:你需要知道的所有术语和定义
NFT的标准:ERC721、ERC1155和ERC-998
NFT的元数据:链上与链下,存储解决方案
NFT艺术超棒的 8 个原因——以及数字创意新经济
NFT 将如何为激情经济提供动力;关于Solana区块链和NFT,你需要知道的一切

