全球数字财富领导者

10 个最常见的以太坊智能合约漏洞

2023-11-10 19:08:40
金色财经
金色财经
关注
0
0
获赞
粉丝
喜欢 0 0收藏举报
— 分享 —
摘要:在区块链领域,完全透明的理念是一场颠覆性的改变,让每个人都能窥视交易和智能合约。但有时,这种开放性可能适得其反。

作者:Sena aslıbay 来源:medium 翻译:善欧巴,金色财经

1.缺乏访问控制

这种漏洞的本质在于缺少对访问的控制。那么,何谓访问控制呢?访问控制是一种安全保护机制。举例来说,假设你的智能合约中有一个函数,而这个函数只应该由你来执行。在这种情况下,任何其他人都不应该能够触发该函数。为了解决这个问题,你需要为函数添加一个修饰器或使用类似下面这样的 require 语句:

带修饰符的第一个选项;

UzvbQoNa2vtcJUJsE0DMJu36zQICKgCiRZ5GQ0BN.png

带有 require 行的第二个选项;

z47BWE2ierq2TJEAg9ZokBqcWZnPqZ2e5GhFHJMM.png

如果你忘记了这种控制,任何人都可以运行你的函数并根据他们的喜好使用你的合约。

2. 误触发事件

区块链领域,完全透明的理念是一场颠覆性的改变,让每个人都能窥视交易和智能合约。但有时,这种开放性可能适得其反,引发我们所谓的“误触发事件”。这些事件发生在智能合约意外地触发外部系统的操作。通常,这是编码错误的结果,可能使操作偏离轨道,甚至导致财务损失。

为了避免误触发事件,你必须仔细检查代码,验证数据,并在各种场景中对智能合约进行测试。此外,密切关注那些外部系统和预言机可以帮助最小化风险。了解误触发的原因以及其重要性对于保持区块链应用的可靠性和可信度至关重要。

3. 不安全的随机性

众所周知,计算机本质上不擅长生成真正随机的数字。因此,它们通常依赖算法对某些数据进行混淆和近似随机性。在区块链系统中也存在类似的挑战,这种方法可能导致重大问题。例如,在一个旨在在一场机会之战中选择随机获胜者的智能合约中,需要使用一个随机函数。在这种情况下,用于生成随机性的值可能包括已解决函数的当前难度级别、当前时间戳,有时还包括与智能合约交互的用户的公共地址。

o8ZXhd8K0cgbnK5tEqqgzukcPWcoOVBQtpsEmGt9.png

虽然这些参数可能足以生成看似随机的数字,但必须记住,在区块链系统中,每个人都可以访问代码。另外,值得注意的是,区块链网络上的交易并不总是在您发起交易时立即发生。他们不遵循“先进先出”(FIFO) 规则的系统。相反,天然气费较高的交易通常会被优先考虑并首先处理。在这种情况下,请考虑以下情况:合约所有者触发随机函数来确定获胜者。然而,该交易已添加到内存池(待处理交易的等待区域),但尚未解决,因为内存池尚未满。与此同时,另一位参与者使用当前时间戳、函数的难度级别,和他们的公共地址,设法预测随机数,从而赢得胜利。重要的是要记住,合约所有者的交易尚未处理,并且他们提供的 Gas 量对区块链上的每个人都是可见的。在这种情况下,参与者可以增加他们的天然气费以超过合同所有者,并确保他们的交易在合同所有者之前得到处理,从而确保获胜。

虽然没有万无一失的解决方案来解决这个问题,但实施燃气费限制和具有最高燃气费的处理功能可能是一个潜在的解决方案。但有人可能会说,既然这种情况是合法的,那么与其采取此类措施,不如让开发出更快机器人的用户获胜。

4.冗余代码

区块链中,众所周知,我们编写的合约是由矿工处理的,我们支付矿工费作为回报。该汽油费根据我们编写的代码的大小和变量的长度而变化。在这种情况下,我们可以讨论一个称为“冗余代码”的漏洞。虽然此漏洞是信息级别的,但它会严重影响 Gas 费用,导致不必要的费用支付。冗余代码,顾名思义,是由于分配给变量的过多空间和不影响输出的不必要的代码行而产生的。

解决方案在于采用干净的编码实践

5.DoS 与区块 Gas 限制

除了与燃气费相关的漏洞之外,区块链系统还可能面临其他挑战,例如拒绝服务(DoS)攻击。在此类攻击中,恶意行为者可能会使智能合约无法运行。但他们是如何实现这一目标的呢?考虑这样一个场景,您在智能合约中定义了一个没有限制的变量(例如数组),并且该变量由外部触发的函数填充。攻击者通过过度填充变量来利用这一点。稍后,如果您尝试使用 for 循环遍历该数组,则需要处理与数组大小一样多的操作。这可能会导致 Gas 费超过 Gas 费上限,从而导致您的智能合约无法运行。虽然在这种情况下您不会损失金钱,但您编写的代码会变得无效。

防止这种情况的方法可能是尽量减少 for 循环的使用,并在使用字符串或数组等变量时设置限制。

6. 前置交易

正如我们在“不安全的随机性”标题下讨论的那样,区块链中的交易不是按先进先出(FIFO)的方式处理的。相反,提供更高燃气费的人会被优先处理。一些人通过注意到即将在市场上发生的重大加密货币购买来利用这一点。他们意识到一旦发生大额购买,市场数据将发生变化,因此他们在此之前进行了大额购买,并提供比重大购买更高的燃气费。这使他们能够以相对有利的价格购买加密货币,然后在所有交易执行完毕并市场发生变化后,迅速出售他们所获得的加密货币。通过这种方式,进行了大额购买的人最终以略高的价格购买了这些币,而前置交易者则获得了额外的利润。我们可以将在此类交易之前采取行动以提前看到和行动的情况称为“前置交易漏洞”。

为了避免这种情况,如果你要进行重要的交易,建议将燃气费设置得尽可能高,并考虑进行分段购买。

7. 整数下溢/上溢

正如我们在“冗余代码”标题下讨论的那样,定义不必要的额外空间可能导致燃气费增加。然而,在使用变量时,我们还需要在变量大小可能导致意外后果的情况下保持谨慎。例如,让我们考虑 uint(无符号整数)变量类型。在使用此变量类型时,如果处理不当,可能会导致与智能合约交互的人的余额发生显著变化。

让我们用一个例子来说明:假设你已经定义了一个 uint 类型的余额变量,表示智能合约中个人的余额。假设用户有10个硬币并试图提取11个硬币。在这种情况下,我们期望 uint 变量会抛出一个错误,类似于“我不能接受负值。” 但实际情况并非如此。根据 uint 变量的大小,它开始计数到可能的最大值,并且用户的余额可能会意外地从10个硬币跳到2562173827368(一个假设的数字)。

dTlCNIrcRQa3jI3LPsLEGIiE9orUmAZGwly6YJSB.png
uAcZIaEohgem88LpylIncMbjroWd4HpX55REWIWt.png

防止这种情况相对简单。只需一个 require 语句就可以解决这个问题。此类漏洞称为整数上溢/下溢漏洞。

8.可重入性

可重入漏洞更具技术性。例如,假设您编写了一个智能合约,并且在该智能合约内,提款函数中有一行代码,该代码使用该函数call()允许用户提取资金。用户可以是个人或另一个智能合约。让我们考虑用户是另一个智能合约的情况。当用户触发提现功能时,call()在用户方面,函数将调用用户智能合约的后备函数(即使尚未显式创建后备函数,它也会遵循规则默认以空运行)。在这种情况下,如果恶意用户在后备函数中插入新的提款线,他们就可以进入循环并耗尽智能合约中的所有资金,从而导致重大损失。因为当withdraw函数执行时,它会call()再次触发该函数,进而调用该fallback()函数,从而导致循环。这允许提取智能合约中的所有代币。

攻击者智能合约

qEsqRhp0j4HPM4YIYpjR0uhOZFpwCZno7pknP96G.png

受害者智能合约

L7dHrDG8pXwDFWJ2NEvsvzEFHBA1MBf0f72ewy7W.png
xtzr8XRg8dH7Z2XcgyuOlRmTcSttBPrfmKpGCqCR.png

9. 默认函数可见性

在Solidity中,如果你没有为你编写的函数指定任何可见性修饰符,它将默认为public可见性。这意味着一些不应该从外部源触发的函数可能由于不正确的可见性设置而意外变为外部可调用。这不仅可能导致数据丢失,还可能导致过多的燃气消耗。让我们考虑一种情况:你有一个函数,当从外部调用时不会带来任何安全风险,但主要用于代码内部使用。在这种情况下,为该函数指定internal可见性是至关重要的。如果将其声明为public,与internal相比,它可能会产生更高的燃气成本。同样,如果一个函数只能从外部源调用,请将其声明为external可以在与其交互时导致较低的燃气费用。

10. 未经检查的返回值

如前所述,在Solidity中,个体可以调用智能合约,发送和接收以太币。在这种情况下,你可以使用call()、callcode()、delegatecall()、send()等函数。然而,重要的是要记住这些函数只返回true或false。因此,在代码中使用这些函数时,你必须进行验证检查。如果没有验证事务是否返回了true或false,你可能会使攻击者有可能将硬币转移到他们的余额中。在这种情况下,进行此验证是至关重要的,以确保在继续之前已成功发送或接收了资金。

WvywljKNQq1RydHSUB6HHivHBMiJl580BSlmaQaS.png

在这种情况下,建议使用transfer()函数而不是这些函数。这是因为transfer()函数在出现任何错误时会自动回滚整个事务。这种行为确保如果出现任何问题,所有操作都会被回滚。

来源:金色财经

敬告读者:本文为转载发布,不代表本网站赞同其观点和对其真实性负责。FX168财经仅提供信息发布平台,文章或有细微删改。
go