引言
鉴于以太坊上 NFT 生态系统的繁荣以及相关安全问题的增加,我们将发布一系列博客文章,介绍这些安全问题,并为开发人员提供一些保护智能合约的建议。在本篇博客中,我们将介绍 NFT 合约 中的重入问题以及如何缓解相关的漏洞。
什么是重入
根据 维基百科
在计算中,一个计算机程序或子程序如果能够安全地在多个处理器上并发运行,或者在单处理器系统上,一个可重入过程可以在执行中途被中断,然后在之前的调用完成执行前被安全地再次调用(“重入”),则称该程序或子程序是可重入的。
在智能合约中,当一个函数执行另一个合约的外部调用,而该外部调用在原始函数返回之前进一步调用了原始函数(或其他函数)时,就可能发生重入。如果外部调用是由不受信任的实体(例如恶意合约)控制的,则可能导致意外结果。这是因为,在某些函数中,结果取决于合约状态。合约状态将在外部调用后更新。然而,该函数的重入调用将使用旧状态而不是更新后的状态。
重入一直是智能合约中普遍存在的问题,例如 MakerDAO 攻击,Uniswap 上的 ERC777,以及其他(请在 BlockSec Academy 上搜索 Reentrancy)。
NFT 中的重入

对于 NFT 合约,存在一些开发人员可能忽略的隐式外部函数调用。它们包括 onERC721Received 和 onERC1155Received 函数。onERC721Received 函数用于检查接收方合约是否能够处理 NFT(以防止 NFT 被永久锁定)。该函数在 ERC721 合约 的 safeTransferFrom 和 _safeMint 中被调用。 ERC1155 合约 中也存在类似的函数。由于这些外部函数调用,重入可能在未被合约开发人员注意到的情况下发生。
单函数重入
单函数重入是重入攻击的较简单形式。在这种类型的重入攻击中,被重入的函数与原始函数相同。攻击者可以在函数第一次调用完成之前反复调用该函数。在 NFT 合约中,这种情况通常发生在与 mint 操作相关的函数中。
例如,一些 NFT 项目可能允许每个用户免费铸造 NFT,设定整个项目的最大供应量,或者设定单个用户可以持有的 NFT 的最大数量。通常,这些限制是在实际铸造操作之前进行检查的。但如果与这些限制相关的状态是在 safeMint 函数之后更新的,攻击者就可以重入该铸造函数并绕过限制,因为相关状态与该函数的第一次调用是相同的。我们之前博客文章中发布的 HypeBears 重入攻击是一个例子。
一个更复杂的单函数重入发生在 safeMint 函数在循环中使用,并且限制在循环开始前检查。在这种情况下,即使在外部调用之前一些状态已经被自动更新,循环中剩余的 safeMint 函数调用仍然可以绕过验证,因为循环已经开始,而验证发生在循环开始之前。
例如,在另一篇 文章 中展示的示例中,mintNFT 函数会检查用户想要铸造的 NFT 数量加上当前供应量是否会超过最大供应量。safeMint 函数在 onERC721Received 外部调用之前更新总供应量。攻击者仍然可以利用这一点,因为 safeMint 函数每次只将总供应量增加 1。因此,如果攻击者在循环中第一次调用 safeMint 时重入 mintNFT 函数,总供应量将变为旧供应量加 1,而不是旧供应量加上 mintNFT 第一次调用将要铸造的 NFT 数量。
跨函数重入
攻击者不是进入相同的函数,而是可以进入与原始函数共享或依赖于状态的另一个函数。我们在之前的博客文章中详细介绍了一些案例,例如 Revest 案例 和 OMNI 案例。
总结和建议
以下是一些为 NFT 智能合约开发者提供的建议,以缓解重入威胁。
- 在代码中 使用检查-效果-交互模式。
- 在使用任何会引入外部调用的第三方库时要小心。对于 NFT 合约,要警惕
onERC721Received和onERC1155Received函数的隐式回调。
阅读本系列的其他文章
关于 BlockSec
BlockSec 是一家开创性的区块链安全公司,成立于 2021 年,由一群在全球享有盛誉的安全专家组成。公司致力于提高新兴 Web3 世界的安全性和可用性,以促进其大规模采用。为此,BlockSec 提供智能合约和 EVM 链 安全审计 服务,用于安全开发和主动阻止威胁的 Phalcon 平台,用于资金追踪和调查的 MetaSleuth 平台,以及供 Web3 构建者在加密世界高效冲浪的 MetaSuites 扩展。
截至目前,公司已为 MetaMask、Uniswap Foundation、Compound、Forta 和 PancakeSwap 等 300 多家尊贵客户提供服务,并获得了 Matrix Partners、Vitalbridge Capital 和 Fenbushi Capital 等知名投资者两轮融资,总计数千万美元。
官方 Twitter 账号:https://twitter.com/BlockSecTeam



