智能合约安全考虑

2025 年 6 月 2 日 | 阅读 7 分钟

开发人员需要确保智能合约是安全的,并且不能被黑客攻击或篡改。没有人应该能够从智能合约中提取资金。

一些攻击是

  1. 溢出和下溢攻击
  2. 重入攻击
  3. 事务发起者攻击
  4. 跨函数竞争攻击

在本教程中,我们将简要介绍每一种攻击的细节、定义、发生方式以及如何防止它。

让我们开始吧!

溢出和下溢攻击

当用户为变量分配超出变量声明数据类型的值时,就会发生这种情况。

溢出攻击

如果分配给变量的值大于变量的单位范围,则会导致溢出错误。让我们通过一个例子来理解这一点

溢出攻击示例

考虑一个无符号 8 位整数变量 uint8 bal。我们都知道 8 位变量的范围是 0 到 255。让我们看看如果我们将变量“bal”的值增加 1 会发生什么。

输出

Smart Contract Security Consideration

说明

变量 bal 的值为 255,将其增加 1 会使其变为 256,因此它将超出范围(0-255),并将导致溢出错误。始终记住变量单元的范围。

注意:Solidity 不提供任何直接的方法或指示来检测溢出错误。因此,开发人员必须意识到这个问题,并确保他们的代码考虑到这些可能的错误。这可以通过使用安全数学库或使用适当的类型(例如 uint256)进行整数运算来实现。

下溢错误

下溢是溢出攻击的相反。如果分配给变量的值小于变量的单位范围,则会导致下溢错误。与溢出攻击相比,下溢攻击在智能合约中发生的频率更高,有时开发人员会变得过于激动,因为它无法被检测到。

  • 如果您的合约没有任何处理和检查下溢和溢出的规定,攻击者可以获得比他们拥有的更多的代币。
  • 他们还可以获得最大余额,这本质上是盗窃。
  • 这些错误的结果可能是灾难性的,并且可能破坏整个系统,因为最大化的代币数量与代币数量不同。

下溢攻击示例

如果我们将采用与溢出相同的示例,并使用无符号 8 位整数变量 uint8 bal。我们都知道 8 位变量的范围是 0 到 255。让我们看看如果将变量 bal 的值减 1 会发生什么。

输出

Smart Contract Security Consideration

说明

变量 bal 的值为 0,将其减 1 将使其变为 -1,因此变量 bal 的新值将不属于范围 (0-255),并将导致下溢错误。

如何防止溢出和下溢攻击

以下是防止溢出和下溢攻击的一些方法

  1. SafeMath 库: SafeMAth 库是提供溢出检测的方法之一。它包含所有基本算术运算,但它也检查先决条件和后置条件,以确定是否发生了溢出
  2. 最新编译器版本: 可以通过使用现代编译器来防止溢出和下溢攻击。
  3. 使用修饰符 onlyOwner: 开发人员可以在其智能合约中添加检查,以确保值永远不会超出其预期范围或使用修饰符
  4. 定期更新代码: 开发人员应定期更新程序。

重入攻击

Smart Contract Security Consideration

在这种攻击中,智能合约攻击受害者的智能合约并提取全部金额。这是一种攻击,通常发生在智能合约调用另一个合约,而该合约再次调用原始合约,并且它继续触发无限循环。

重入攻击是一种利用智能合约中的漏洞的方法,该漏洞允许攻击者重复调用合约中的一个函数,从而导致无限循环并可能窃取资金。

重入攻击的类型

在智能合约中,重入攻击有两种类型,虽然在这里我们将讨论另一种名为跨合约攻击的攻击。

1. 单重入攻击

顾名思义,当该函数正是攻击者试图递归调用的相同函数时,就会发生单重入攻击。它们很容易调用,甚至很容易在您的智能合约中防止。

2. 跨函数攻击

仅当一个易受攻击的函数与另一个对攻击者具有理想效果的函数共享状态时,跨函数重入攻击才可行。跨函数攻击更难检测,也更难预防。

3. 跨合约攻击

当一个合约中的状态在另一个合约中被调用,但在它被完全更新之前,就会发生跨合约重入攻击。当多个合约手动共享同一个变量,而一些合约不安全地更新共享变量时,就会发生跨合约重入攻击。

重入攻击示例

说明

在上面的合约 ReentrancyExample 中,我们创建了一个名为 depositFucntion 的函数,该函数使用户能够将付款存入他们的帐户,以及另一个名为 withdrawAmount 的函数,该函数允许用户提取他们的金额。由于我们的合约确实检查了重入攻击,因此任何具有恶意瞬间的黑客都可以创建一个合约,该合约将在调用 withdrawAmount 函数之前调用 depositAmount 函数,从而成功地将资金从用户合约转移出去。

如何防止重入攻击

可以通过以下方法防止重入攻击

  1. 在调用外部代码之前更新余额
  2. 确保没有与函数关联的重入修饰符 (nonReentrant)
  3. 防止重入攻击的另一种方法是使用拉取支付方法。 拉取支付是一种更安全的端到端交易,它使用中间托管来发送付款,并避开了与任何潜在恶意服务器的直接联系。
  4. 尝试在您的智能合约中使用 require 语句。 require 语句可以在允许一个函数执行之前轻松检查 solidity 合约的状态。

事务发起者攻击

Smart Contract Security Consideration

事务发起者攻击是一种网络钓鱼攻击,攻击者从智能合约中窃取所有资金。当用户使用 tx.origin 而不是 msg.sender 时,就会发生这种情况。

程序

说明

正如您所看到的,上面的程序侧重于滥用 tx.origin 进行身份验证。任何黑客都可以轻松部署一个恶意合约,该合约欺骗任何程序员调用 tx.origin。部署后,此合约仅有权与主合约交互,其中 tx.origin 保存原始用户的地址。由于攻击者拥有该地址和各种其他权限,他们可以轻松更改代码并进行恶意活动。

情况与小偷对你熟人的朋友采取行动一样。当你的亲戚敲门时,小偷溜了进来,因为你没有核实他们是谁。

如何防止事务发起者攻击

  1. 在您的智能合约中,如果您想检查所有权授权,请使用 **msg.sender** 变量而不是 **tx.origin**。
  2. 停止使用 **address.call.value(amount)()** 函数,您可以改用 **address.transfer()** 函数。
  3. 函数 **address.transfer()** 只有 2300 的气体津贴 - 也就是说,一旦即使被发出,可能的攻击合约将没有足够的燃料用于进一步的计算。
  4. 函数 **address.transfer()** 也可以在失败时被抛出

跨功能竞争攻击

在跨功能竞争攻击中,两个不同的外部方试图调用两个不同的函数来更新同一个变量。

示例:程序演示在 Solidity 智能合约中进行跨功能竞争攻击,

说明

上面的程序与 **竞争条件** 相同,程序员编写了多个函数来同时更改同一变量(年龄)的值。

  • 如果用户 x 和用户 y 调用 setAge 并尝试同时更新年龄,则最后完成的函数将最终更新年龄的值。
  • 这种条件之所以发生,是因为智能合约无法同时处理两个不同的顺序操作。

结论

在以太坊区块链网络中,保护智能合约至关重要。由于合约使用 gas 费运行,并且经常管理有价值的资产,如果它面临任何安全威胁或不需要的网络钓鱼攻击,它将导致重大损失。深入了解不同的安全威胁,例如 **溢出和下溢攻击、重入攻击、事务发起者攻击和跨功能竞争攻击,** 开发人员可以轻松地防止它们,并可以提高 Solidity 智能合约的弹性。