W3
06安全审计进阶10 个步骤4 个小节预计 50-60 分钟v1 · 更新于 2026-09-19

合约安全:重入、预言机操纵等高频漏洞与防范

以攻击者视角系统拆解导致史上巨额损失的智能合约漏洞类别:建立「所有外部输入与外部调用都是恶意的」安全第一性原理;重入攻击的三种形态与 Checks-Effects-Interactions、重入锁;整数溢出/下溢与 0.8 内置检查;访问控制缺失与 tx.origin 钓鱼;外部调用失败导致的拒绝服务;闪电贷如何无成本放大价格预言机操纵风险,以及 Chainlink 喂价与 TWAP 的正确姿势;无限授权与签名重放;可预测随机数;最后用 The DAO、Parity 多签、Ronin 桥、Euler 等真实案例时间线建立直觉,并给出项目方的应急响应预案框架。

合约安全:重入、预言机操纵等高频漏洞与防范
⚠️
本章要点提示

本章讲解攻击原理的唯一目的是防御:所有示例均为已公开的历史漏洞与标准教学案例,切勿对未授权的合约尝试任何攻击——未授权访问计算机系统在绝大多数司法辖区属于刑事犯罪。作为开发者,请把本章每一条对应到自己的代码清单;作为用户,请对未审计、无限授权、管理员权限过大的项目保持最高警惕。

小节 01

安全思维与重入攻击

步骤 13
01

安全第一性原理:默认一切外部输入都是恶意的

登录记录进度

合约安全的思维方式与普通业务开发完全相反,核心假设只有一条:所有外部输入都是恶意的,所有外部合约都可能反向攻击你。具体展开为四条原则: 1. 不要信任任何输入:用户参数、调用者地址、其他合约的返回值、甚至链上「当前价格」「当前时间」都可能被操纵。

  1. 不要信任任何外部调用:调用外部合约后,对方的 fallback / receive 可以执行任意逻辑,包括回调你的函数。 3. 权限最小化:默认函数是 external/non-payable,默认没有任何管理特权;每个 owner 权限都要问「如果这个密钥被盗会怎样」。

  2. 深度防御:编译检查、require 校验、重入锁、多签、Timelock、监控告警层层叠加,任何一层失效都不应导致资金立即损失。

后续每类漏洞都是某条原则被违反的具体形态。

安全第一性原理:默认一切外部输入都是恶意的
安全第一性原理:默认一切外部输入都是恶意的|界面示意 · ethereum.org · 采集于 2026-09-18
💡
小技巧:

Code review 时带着「如果我是攻击者,我会怎么喂参数给这个函数」的视角逐行过,比顺着作者思路读有效十倍。

02

重入攻击:The DAO 6000 万美元事件的原型

登录记录进度

重入(Reentrancy)是最经典的合约漏洞:当合约先给外部地址转 ETH(或转代币)、后更新内部余额时,收款方可以在收款回调里再次进入同一个函数,把「还没更新的余额」反复提取。

攻击模板:受害合约 withdraw()call{value: balance}("")balances[msg.sender] = 0;攻击者在 receive 里再次调用 withdraw(),此时余额尚未清零,递归提空整个合约。

The DAO(2016 年)正是因此被抽走近 360 万 ETH,直接导致以太坊硬模分叉出 ETH 与 ETC。

三种现代形态:单函数重入、跨函数重入(两个函数共享同一状态)、跨合约重入(ERC-777 钩子、ERC-721 回调)。

重入攻击:The DAO 6000 万美元事件的原型
重入攻击:The DAO 6000 万美元事件的原型|界面示意 · ethereum.org · 采集于 2026-09-18
🚫
避坑提醒:

NFT 转账的 onERC721Received、ERC-777 的 tokensReceived 等回调都是重入入口;凡是带外部回调的转账都按高危处理。

03

重入防御:CEI 模式与重入锁

登录记录进度

两道标准防御,且推荐同时使用: 1. Checks-Effects-Interactions(检查-生效-交互):把函数严格分成三段——先 require 检查条件,再更新所有内部状态(余额清零、计数扣减),最后才与外部合约交互(转 ETH、转代币)。

这样即使被重入,第二次进入时看到的已是更新后的状态,检查会失败。 2. 重入锁 ReentrancyGuard:OpenZeppelin 提供的修饰器,合约里有一个 _status 变量,nonReentrant 修饰的函数执行期间再次进入会直接 revert。

对所有「对外转账 + 改状态」的函数统一加锁。 solidity function withdraw() external nonReentrant { uint256 amount = balances[msg.sender]; // 检查 require(amount > 0, "zero"); balances[msg.sender] = 0; // 生效(先清零) (bool ok, ) = msg.sender.call{value: amount}(""); // 交互(最后转钱) require(ok, "send failed"); }

重入防御:CEI 模式与重入锁
重入防御:CEI 模式与重入锁|界面示意 · docs.soliditylang.org · 采集于 2026-09-18
💡
小技巧:

读历史代码时先搜 call/transfer 与外部转账语句,再倒回去看状态更新的位置——顺序错误几乎等于直接发现重入漏洞。

小节 02

数值与权限类漏洞

步骤 45
04

整数溢出/下溢与精度问题

登录记录进度

Solidity 0.8.0 之前,uint 是回绕整数:0 - 1 会变成 2^256-1,攻击者能用一笔「下溢」把自己的余额变成天文数字(BeautyChain BEC 代币因此归零)。

0.8 起编译器自动插入溢出检查,算术越界直接 revert,这类裸漏洞已基本消失,但衍生问题仍高频: 1. 在 0.8 项目里引入未检查的汇编或 unchecked { }——为省 Gas 手动关闭检查时必须自行论证边界。

  1. 精度截断与舍入a * b / c 的整数除法永远向下取整,错误顺序(先除后乘)会让小额操作精度归零,奖励计算被慢慢偷走。原则是先乘后除,必要时提高内部精度(如用 1e27 的 RAY 单位)。

  2. 份额会计(LP share)首发攻击:池子第一笔存款若数量极小,可通过操纵份额比例让后来者拿到 0 份额——标准做法是在构造时铸造 1000 份「死份额」。

整数溢出/下溢与精度问题
整数溢出/下溢与精度问题|界面示意 · docs.soliditylang.org · 采集于 2026-09-18
🚫
避坑提醒:

任何 unchecked 块都必须在审计中单独论证;任何乘法/除法组合都要检查中间结果能否溢出或归零。

05

访问控制缺失与 tx.origin 钓鱼

登录记录进度

最高频、最致命、也最不该犯的错误类别: - 漏写 onlyOwner/角色校验mintsetFeerescueTokenspauseupgrade 等管理函数忘了加权限修饰器,任何人都能调用增发或提空资金。

  • 初始化函数未防重入保护:可升级/代理合约的 initialize 任何人都能再次调用,把自己设成 owner——必须用 initializer 修饰器。

  • tx.origin == owner 做鉴权tx.origin 是交易的原始发起者。恶意合约诱导 owner 调用它的一个无害函数,该函数再回调你的合约,此时 msg.sender 是恶意合约、但 tx.origin 仍是 owner,鉴权被绕过。

鉴权永远用 msg.sender。 - approve 竞争条件:改授权额度时先不查旧额度,可被抢跑双花——用 increaseAllowance/decreaseAllowance 或先清零再设。

访问控制缺失与 tx.origin 钓鱼
访问控制缺失与 tx.origin 钓鱼|界面示意 · docs.soliditylang.org · 采集于 2026-09-18
💡
小技巧:

审计第一步:列出合约所有 external/public 函数,逐个标注「谁有权调用」,凡涉及写状态且答案含糊的,都是高危项。

小节 03

经济模型与外部依赖类攻击

步骤 69
06

拒绝服务:外部调用失败与循环打款

登录记录进度

让协议「永久卡死」同样能造成资金永久锁定,两类典型: 1. 依赖外部调用成功:清退/分红函数用循环给所有用户逐个 transfer,其中一个地址是恶意合约(永远 revert)或本身会消耗完 Gas,整笔交易失败,所有人都拿不到钱。

修复:让用户各自主动领取(pull over push,记录应发金额,用户调用 claim),转账用 call 检查返回值并对失败地址做跳过/记账。 2. 遍历无限增长数组:for 循环遍历所有持有人/订单,人数超过区块 Gas 上限后函数永远无法执行完。

修复:用分页处理或让用户按索引自行操作。 3. 外部依赖可被暂停:强依赖的稳定币被发行方冻结(USDT/USDC 合约有黑名单能力),整个协议随之停摆——架构上要意识到这种「合规冻结」风险。

拒绝服务:外部调用失败与循环打款
拒绝服务:外部调用失败与循环打款|界面示意 · docs.soliditylang.org · 采集于 2026-09-18
🚫
避坑提醒:

永远不要写「for 循环遍历所有用户并转账」的函数;push 模式在链上既贵又脆弱,claim 模式是行业标准答案。

07

闪电贷与价格预言机操纵

登录记录进度

闪电贷(Flash Loan)允许在一笔交易内无抵押借出数百万美元,只要交易结束时归还。它本身是中性工具(用于套利、抵押替换),却把「攻击需要本金」的门槛降为零。 重灾区是用现货价格作为定价依据的协议:攻击者借出天量资金 → 在 DEX 池子里砸盘把某代币瞬时价格打歪 → 以失真价格在你的借贷协议超额抵押借款/清算 → 反向交易归还闪电贷,差额进自己腰包。bZx、Harvest、Euler 等事件都是变体。

防御要点:

  1. 绝不用单一 DEX 现货价(spot price)做资产定价
  2. 使用去中心化预言机(Chainlink Data Feeds)并加价格时效与偏差检查。
  3. 必须算链上价格时使用 TWAP(时间加权平均价)且窗口足够长、流动性分散到多池。
  4. 闪电贷防护本身不是目的——检查所有「同一笔交易内可以被瞬时改变、又被你读取」的状态。
闪电贷与价格预言机操纵
闪电贷与价格预言机操纵|界面示意 · ethereum.org · 采集于 2026-09-18
Chainlink Data Feeds 文档
08

无限授权与签名类漏洞

登录记录进度

两类与用户行为直接相关的高频风险: 无限授权:用户为图省事 approve 无限额。一旦被授权合约出现漏洞(或本身是恶意代币/钓鱼协议),攻击者可经 transferFrom 清空该地址持有的对应代币。

防御:协议默认申请有限额度、支持 permit 签名额度;用户应养成定期用 revoke.cash、Etherscan TokenApprove 页撤销不用授权的习惯。 签名类漏洞:链下免 Gas 签名(permit、meta-transaction、挂单)必须防止重放——签名消息中加入域名分隔(EIP-712 的 name/version/chainId/verifyingContract)、nonce、deadline,并在合约侧记录已用 nonce;直接用 ecrecover 还要处理签名延展性(malleability,s 值必须落在下半域)与签名地址为零的情况。

优先使用 OpenZeppelin 的 ECDSA/EIP712 库而不是手写验签。

无限授权与签名类漏洞
无限授权与签名类漏洞|界面示意 · docs.openzeppelin.com · 采集于 2026-09-18
🚫
避坑提醒:

看到「connect 钱包后到某网站领空投,需先签名」要格外小心:EIP-712 以外的盲签可能是在授权移动你的资产,签名前必须读清钱包弹窗里的完整内容。

09

可预测随机数与其他常见暗坑

登录记录进度

一批短小但致命的问题: - 链上没有真随机:用 block.timestampblock.prevrandaoblockhash、矿工地址「生成」的随机数都能被验证者/矿工或同区块合约预测操纵。

链上抽奖、盲盒必须用 Chainlink VRF 之类可验证随机源,或采用「提交-揭示(commit-reveal)」两阶段方案。 - 时间依赖:不要用精确时间差做严格判断,出块时间不固定;时间锁用 block.timestamp 的比较而不是相等。

  • delegatecall 到不可信地址:delegatecall 在调用者的存储上下文执行对方代码,代理实现地址被替换或存储槽错位等于把合约拱手让人。 - 代币回调差异:USDT 不返回 bool、部分代币收手续费、rebasing 代币余额自动变化、ETH 与 WETH 语义不同——集成任何第三方代币前先读它的合约特性文档。

  • 前端与链上不一致:网站显示的「余额/奖励」必须能从链上独立复算,前端可被伪造,安全校验只以链上为准。

可预测随机数与其他常见暗坑
可预测随机数与其他常见暗坑|界面示意 · docs.soliditylang.org · 采集于 2026-09-18
小节 04

真实案例与应急响应

10

真实案例与应急响应预案

登录记录进度

用损失建立敬畏:The DAO(2016,重入)Parity 多签(2017,库合约自毁导致 50 万 ETH 永久冻结)bZx(2020,闪电贷价格操纵)Ronin 桥(2022,多签验证节点被攻破,6.2 亿美元)Wormhole(2022,签名验证缺失,3.2 亿美元)Euler(2023, donateToReserves 缺失健康检查导致可随意追加坏账,约 2 亿美元)。规律高度一致:不是 Solidity 语法难,而是「外部调用顺序、会计守恒、权限、定价来源」被打破。

项目方上线前应预先写好应急响应预案(Incident Response):谁有 pause 权限(多签还是 EOA)、暂停操作 SOP、白帽沟通渠道与赏金政策、官方公告渠道(防止骗子冒名)、链上监控与告警、与交易所/桥冻结资产的联络方式。事故发生后的前 30 分钟决定损失上限。

真实案例与应急响应预案
真实案例与应急响应预案|界面示意 · defillama.com · 采集于 2026-09-18
💡
小技巧:

把这些事件的事后复盘报告(post-mortem)当教材读,每份报告都是价值上亿美元的免费课程,搜索 incident + 协议名即可找到官方复盘。

来源与时效

本章记录平台 ethereum.org · 客户端 Web / App · 版本 v1 · 最后更新 2026-09-19 Web3 产品的界面与规则更新频繁,动手前请以官方当前界面与公告为准。

这一章可以动手试

智能合约域有 1 个练习, 在浏览器里真跑(不连钱包、不发交易),可以拿它们验证刚读到的结论。

相关百科文章

看完本章后可以延伸阅读这些条目