前言
以太坊主网由 PoW 迁移到 PoS 时,社区中部分矿工保留 PoW 算法并催生了 EthereumPoW(ETHW) 的硬分叉。分叉看似只是共识切换,却暴露出大量 智能合约 与 跨链桥 在设计上忽视的细节:重放攻击。
Cobo 安全团队自硬分叉启动后便对两条链网进行 7×24 小时监控,记录到 OmniBridge 与 Polygon Bridge 先后出现资产被异常提取。通过溯源,我们确认核心漏洞并非链底层,而是 签名验证逻辑缺失 chainId。下文将用最小化代码片段拆解攻击思路,并给出协议加固与套利观察。
关键词:ETH硬分叉、重放攻击、OmniBridge、Polygon Bridge、智能合约安全、 DeFi安全、 套利机会、 chainId
重放攻击的两大类型
1. 交易重放
PoW 时代,EIP-155 尚未启用前,一笔签名好的交易可在 chainId 相同的多条链上复现。EIP-155 强制把 chainId 写进签名中,直接堵死了该路径,却留下第二条幽径。
2. 签名消息重放
用户或验证节点给 离链消息(如 Permit、Withdraw、Vote 等)签名时,若消息体不含 chainId,则可在任何分叉链重复验签。攻击流程无需复现完整交易,只需重复 “合法签名 + 过期/重复消息” 即可提币。
案例分析:OmniBridge & Polygon Bridge 被掏空的 72 小时
OmniBridge:validator 的签名被复制黏贴
桥的核心函数 executeSignatures 先验签后解码。
- 解码结果包含
chainIds[1],理论上可拒绝其它链调用。 - 但
sourceChainId()常量存的是 管理员预先写的值,而非block.chainid,因此另一个分叉链只要 validator 相同,签名即能通过。
当 ETHW 分叉完成,OmniBridge 合约状态被完整克隆。攻击者把主网 validator 的离线签名复制到 ETHW,即可无成本提取同样的 token。
Polygon Bridge:headerRoot = proposerReplay
Polygon Bridge 使用 区块证明 + headerRoot 来确认跨链提款。
checkpointManager.submitCheckpoint(...)由指定proposer提交 headerRoot。- 合约只校验
borChainID(Polygon 子链 ID),未校验 EVM 级 chainId,于是同一批proposer签名在两条链都能过关。
攻击者思路:
- 主网 Polygon Bridge 正常 deposit → exit,拿到证明。
- 把
submitCheckpoint签名及参数原封不动发到 ETHW。 - ETHW 上重复调用
exit:因 headerRoot 是合法签名后构造的,合约无法区分链 ID,提款成功。
为什么会漏掉 chainId 校验?
历史原因:
- 早期桥协议仅服务于 单链 ↔ 单链 场景,跨链消息天然隔离,不存在硬分叉。
- 设计文档里甚至明确写:“无需关心 chainId 变化”。
现实教训:
- 硬分叉让 “单链” 场景瞬间变成 “多链同源”。
- 固定
DOMAIN_SEPARATOR的合约(如 Uniswap V2 pool)顿时暴露在重放风险中——分叉后链 ID 变,但常量无法更新。
👉 深入拆解 uniswapV2 pool 如何通过固定 DOMAIN_SEPARATOR 泄露授权
协议层防御清单
所有消息签名必须携带 动态 chainId。
bytes32 digest = _hashTypedDataV4( keccak256(abi.encode(MESSAGE_TYPEHASH, ..., block.chainid)) );- 合约构造函数里 不要 缓存 chainId;验证时再
block.chainid。 - 若协议已部署,可额外增加 nonce 与 fork\_guard 的存储,升级时同时更新。
FAQ
Q1 普通钱包会不会因为分叉丢失 ETH?
不会。私钥控制的所有资产在两条链独立存在,只要不主动与分叉链交互,不会触发重放。
Q2 已拿到 ETHW 空投,怎样才能卖却不被监听到?
你需要先在 干净钱包 内花费少量 ETHW,标记新地址,再分拆转账,避免主网地址暴露。高频交易场景必须先在 ETHW 把该地址 nonce 提升,防止跨链混淆。
Q3 DeFi 项目一键做多 chainId 重构老合约成本高吗?
中等。若代码使用 OpenZeppelin 的 EIP-712 最新 helper 版本,只需将 block.chainid 拼入 digest 即可,gas 增加 < 1%。
Q4 交易所如何应对“幽灵充值”?
交易所需检测回执里 chainId 与预期区块一致,避免把 ETHW 误认为 ETH 存款。
Q5 是否所有消息都必须做重放保护?
基本原则:当签名具有经济价值(透资、提款、授权),就应加入链标识。纯查询、读取类签名可忽略。
Q6 未来再次硬分叉时,有哪些前置检查?
- 评估所有资产桥在旧合约链的数据一致性。
- 发布分叉公告必须为开发者提供 chainId 更新工具,例如 web3.js 的
chainIdForFork(networkName)插件。
结论
“多链”已不只是口号,任何忽略 chainId 的简单设计都可能演变成安全溃坝。 开发者一旦确认路线图将涉及分叉或多链部署,就应在早期把 签名校验、链 ID、nonce 三个因子全部纳入 threat model。Cobo 安全团队建议:
- 立即审计现有 bridge、permit 相关合约;
- 对于不支持的新分叉链 尽早声明立场,避免用户误操作;
- 结合托管与自托管场景,将 重放保护逻辑 下沉到 SDK,降低业务迁移成本。
安全是一次长跑,而重放攻击提醒我们:分叉也可能带来意想不到的套利与危机。保持敬畏,链上无忧。