🍁 金秋送福,大奖转不停!Gate 广场第 1️⃣ 3️⃣ 期秋季成长值抽奖大狂欢开启!
总奖池超 $15,000+,iPhone 17 Pro Max、Gate 精美周边、大额合约体验券等你来抽!
立即抽奖 👉 https://www.gate.com/activities/pointprize/?now_period=13&refUid=13129053
💡 如何攒成长值,解锁更多抽奖机会?
1️⃣ 进入【广场】,点头像旁标识进入【社区中心】
2️⃣ 完成发帖、评论、点赞、社群发言等日常任务,成长值拿不停
100% 必中,手气再差也不亏,手气爆棚就能抱走大奖,赶紧试试手气!
详情: https://www.gate.com/announcements/article/47381
#成长值抽奖赢iPhone17和精美周边# #BONK# #BTC# #ETH# #GT#
智能合约中的重入攻击:理解这种脆弱性并实施预防策略
重入攻击代表了智能合约安全中最臭名昭著的漏洞之一,导致数百万的资金在各种区块链协议中被盗。本文探讨了重入漏洞背后的机制,并提出了安全意识强的开发者应该实施的全面预防技术。
什么是重入攻击?
在其核心,重入攻击发生在智能合约中的一个函数在其之前的执行完成之前被重复调用。当智能合约在解决自己的状态变化之前调用外部合约时,就会产生根本的脆弱性,从而创造出被利用的机会。
在典型场景中,合约A通过调用合约B的某个函数进行交互。当合约B获得在合约A仍在执行其原始函数时回调合约A的能力时,关键的安全漏洞就出现了。这种递归交互模式为潜在的攻击创建了基础。
重入攻击是如何工作的:逐步解析
考虑这种情况:合约A总共持有10个ETH,其中合约B已向合约A存入1个ETH。当合约B试图通过以下序列提取其资金时,漏洞变得可被利用:
关键漏洞在于 余额更新发生在 ETH 转移之后,这使得攻击者可以在余额被设置为零之前多次利用相同的余额。
攻击的解剖:技术实现
让我们来检查一下脆弱的代码模式:
固态性 // 易受攻击的 EtherStore 合约 合约 EtherStore { mapping(address => uint) 公共余额;
函数 deposit() public payable { balances[msg.sender] += msg.value; }
函数 withdrawAll() public { uint bal = 余额[msg.sender]; require(bal > 0);
(bool发送,) = msg.sender.call{value: bal}(“”); require(sent, "发送以太币失败");
余额[msg.sender] = 0; } }
现在,让我们看看攻击者如何利用这个漏洞:
固态性 // 利用重入漏洞的攻击合约 合约攻击 { EtherStore 公有 etherStore;
constructor(address _etherStoreAddress) { etherStore = EtherStore(_etherStoreAddress); }
etherStore.withdrawAll019283746574839201(; } }
函数 attack)( external payable { 需求019283746574839201msg.value >= 1 ether);
etherStore.deposit{value: 1 ether}();
etherStore.withdrawAll019283746574839201(; } }
攻击序列在攻击者调用 attack)( 时开始。这将存入 1 ETH 以建立余额,然后调用 withdrawAll)(。当 EtherStore 发送 ETH 回来时,它会触发 Attack 合约中的 receive)( 函数,该函数在余额更新之前再次调用 withdrawAll)(。这一循环持续进行,直到 EtherStore 的资金被抽空。
全面预防技术
注重安全的开发者可以实施三种强大的技术来防止重入漏洞:
) 1. 功能级保护:非重入修饰符
在个别功能级别上最常见的保护措施是实现重入保护:
智能合约语言 合约 ReentrancyGuard { bool private locked = false;
修饰符 nonReentrant() { require###!locked, “可重入调用”(; 锁定 = true; _; 锁定 = false; }
function withdrawFunds)( public nonReentrant { // 函数代码防止重入 } }
这种方法有效地锁定合约在函数执行期间,防止任何递归调用,直到函数完成并解锁状态。
) 2. 跨功能保护:检查-效果-互动模式
这个基础安全模式重构代码,以消除多个函数中的漏洞:
坚固 // 易受攻击的实现 函数 withdrawAll() public { uint bal = 余额[msg.sender]; require###bal > 0(;
)bool发送,( = msg.sender.call{value: bal})“”(; require)sent, "发送以太币失败"(;
余额[msg.sender] = 0;交互后的状态更新 }
// 安全实施 函数 withdrawAll)( public { uint bal = 余额[msg.sender]; require)bal > 0(;检查
余额[msg.sender] = 0;效果 )state changes(
)bool发送,( = msg.sender.call{value: bal})“”(;相互 作用 require)sent, "发送以太币失败"(; }
通过遵循 检查-效果-交互 模式,合约在任何外部交互之前更新其状态,确保即使攻击者试图重入,他们也会遇到更新后的状态 )零余额(。
) 3. 项目级保护:全球重入保护
对于具有多个交互合同的复杂项目,实现全局重入保护可以提供系统范围的保护:
固态性 合约 GlobalReentrancyGuard { bool 私有 _notEntered = true;
修饰符 globalNonReentrant() { require###_notEntered, “ReentrancyGuard: reentrant call”(; _notEntered = false; _; _notEntered = 真; } }
// 项目中的所有合约都继承自 GlobalReentrancyGuard 合约 SecureContract 是 GlobalReentrancyGuard { function vulnerableOperation)( public globalNonReentrant { // 保护整个项目免受重入攻击 } }
这种方法在保护DeFi协议中的跨合约重入攻击方面尤其有价值,因为多个合约相互之间进行交互。
重入漏洞的现实影响
重入漏洞导致了区块链历史上最具破坏性的攻击之一。臭名昭著的2016年DAO黑客事件导致约360万ETH被盗,当时价值约)百万,最终导致以太坊硬分叉,创造了以太坊经典。
最近,在2020年,Lendf.Me协议因重入攻击损失了大约(百万,突显出尽管人们的意识有所提高,这些漏洞仍然对智能合约安全构成重大风险。
安全最佳实践
除了提到的具体技术,开发者还应遵循以下额外的安全实践:
通过实施这些防御技术和遵循安全最佳实践,开发者可以显著降低重入攻击的风险,并为区块链应用构建更安全的智能合约。