
什么是重入攻击(代码漏洞如何重复提款)
重入攻击是什么:把“外部调用”变成了“插队重复结算”
重入攻击(Reentrancy)本质上不是“黑客很会算”,而是合约在一次业务流程还没结算完时,就把控制权交给了外部合约或外部地址的回调逻辑,导致对方能“重新进入”原合约的同一函数或相关函数,重复触发同一段提款/转账/赎回的结算路径。
在结构上,它利用的是一种常见但危险的执行顺序:合约先对外转账(或调用外部合约),然后才更新自身的余额、份额、已领取记录等“内部账本”。如果外部对象在收到资产的同时触发回调,再次调用原合约,就可能在内部账本尚未更新前重复通过检查,从而出现“同一份余额被多次支出”的效果。
它在安全结构中的作用(从攻击者视角)是“放大一次合法路径的结算次数”;从防守视角看,它暴露了合约状态机设计的漏洞:把不可控的外部执行插入到关键状态更新之前。
为什么重要:重入攻击通常不是单笔损失,而是把池子里可被提取的资产持续抽干,直到合约余额耗尽或触发失败。用户的影响不仅是资金损失,还可能引发连锁反应:资金池枯竭、赎回失败、清算逻辑异常,甚至造成整个协议短时间不可用。
最常见的误解是把它理解成“重复点击提款按钮”或“链上交易能回滚”。链上交易一旦被确认,通常无法像传统系统那样撤销;也正因如此,才会出现类似“什么是被盗回滚(为何多数链无法回滚交易)”这类讨论——多数情况下,协议只能通过升级、暂停或事后补救来应对,而不是指望把历史交易抹掉。
它在合约机制里怎么发生:状态更新顺序与可重入点
理解重入攻击,关键是看合约的“状态更新顺序”。一个典型的资金合约包含三类步骤:
1)检查:你是否有足够余额/份额、是否满足冷却期、是否通过权限等;
2)交互:对外转账、调用其他合约(例如把资产从金库转给用户);
3)效果:更新内部状态(扣减余额、标记已领取、更新总份额)。
当合约在“效果”之前就发生“交互”,就出现可重入点:外部合约在被调用时可以执行任意逻辑,其中就包括再次调用原合约的提款函数。由于原合约的内部状态还停留在“提款前”,第二次进入时检查仍然可能通过,于是再次转出资产。重复多次就形成“循环提款”。
在更复杂的 DeFi 结构里,重入不一定只发生在“同一个函数”。它也可能通过跨函数、跨模块触发:比如先进入赎回函数,再回调进入借贷、换汇或结算函数,利用共享的内部变量或未及时更新的全局状态,制造多次可兑现的余额。
它在安全结构中的作用,是揭示“外部调用不可控”这一事实:合约作者无法假设外部合约会乖乖返回,也不能假设外部调用期间自己的状态不会被再次访问。为什么重要:很多协议的关键资产都在合约里,任何一次“先付款后记账”的顺序错误,都可能把资产池变成可被重复结算的提款机。

常见误解还包括:
– 误解一:只有转 ETH 才会重入。实际上任何外部调用(包括调用代币合约、金库合约、策略合约)都可能成为入口。
– 误解二:只要做了审计就绝对安全。审计能降低风险,但覆盖不了所有组合路径与上线后的环境变化,这也是“为什么审计通过也不代表安全(结构性风险未覆盖)”会被反复提起的原因。
与其他安全与风险术语的关系:不是孤立漏洞,而是系统性连锁
重入攻击经常与其他链上结构耦合,导致后果更难预期:
与闪电贷(Flash Loan):闪电贷的本质是“在同一笔交易内借入—使用—归还”的原子性融资结构。它在安全结构中的作用是让攻击者无需长期持有资金,也能在单笔交易里放大资金规模,从而更容易触发某些阈值(例如需要大额赎回才能显著影响状态)。重要性在于:当重入漏洞存在时,闪电贷可能放大一次交易内的可提取金额与可尝试次数,加速资金池被抽干。常见误解是把闪电贷等同于“免费资金”或“系统漏洞”,它本身是中性的交易原子性工具,风险来自与漏洞或错误定价的组合。
与 MEV / 三明治攻击:MEV 指区块生产者或搜索者通过排序、插入、重排交易获得额外收益的结构性空间;三明治攻击是其中一种典型形态,通过在用户交易前后各插入一笔交易来影响成交价格。它们与重入的关系不在“重复提款”,而在“交易执行环境不可控”:当协议在关键步骤依赖即时价格、即时余额或某种顺序假设时,MEV 可能改变你以为的执行先后与可见性。重要性在于:即便修复了重入,若协议仍把外部状态当作稳定输入,也可能被排序优势放大损失。常见误解是把 MEV 当成单一攻击者行为,实际上它是链上公开内存池与可排序执行带来的结构性现象。
与预言机(Oracle):预言机是把链下或链上其他市场的价格/数据喂给合约的机制。它的重要性在于:很多协议的赎回、清算、抵押率计算依赖预言机数据。重入攻击若发生在依赖价格快照或时间窗口的流程中,可能与“数据更新时点”叠加,让协议在同一交易里反复使用旧快照或未更新的内部状态。常见误解是把预言机当作“一个价格接口”,忽略了它的更新频率、延迟、容错与数据源结构。
与 Rug Pull / 庞氏结构 / 跑路盘 / 挤兑:这些更多是经济与治理层面的风险,而非代码级漏洞。Rug Pull/跑路盘强调的是项目方通过权限、后门、撤池等方式带走流动性;庞氏结构强调收益来源依赖后续资金流入;挤兑则是当用户同时赎回导致流动性不足、按顺序结算时后者无法兑付。重入攻击与它们的共同点在于都能导致“无法兑付”的现实结果,但机制完全不同:重入是执行顺序漏洞导致的非授权超额支出;挤兑是流动性与期限错配导致的支付失败。常见误解是把所有损失都归为“被黑”或“跑路”,从而忽略应对方式与责任边界的差异。
用户最该抓住的要点:它暴露的是“状态机设计”而不是某个按钮
重入攻击之所以反复出现,是因为它触及智能合约最核心的结构:状态机如何从 A 过渡到 B。只要在过渡过程中把控制权交给了外部世界,就必须假设自己会被再次调用。
它的重要性对用户体现在三点:
– 资产托管在合约里,漏洞影响的是“池子级别”的公共资金,而不只是某个地址;
– 一旦发生,链上不可逆使得追回难度高,后续往往只能依赖暂停、升级或社区协调;
– 即便协议没有明显的“高收益噱头”,也可能因为一次顺序错误而遭受系统性损失。
最容易被误解的一点是:把安全当作“有没有黑客”的问题。更准确的理解是:安全是协议机制能否在最坏执行环境下仍保持正确结算。重入攻击正是用最直接的方式提醒人们:在链上,外部调用不是“函数返回”,而是一次把命运交给对方合约的交互;如果账本更新不在前面,重复结算就会发生。



