一句话版本
"用钱包签一条消息来证明地址是你的"(Sign-In with Ethereum,EIP-4361)比密码更安全,但前提是这条消息里写清楚了给谁签、什么时候签、只能用一次。 少了任何一项,攻击者都能把你的签名拿去重放:换个网站用、过一个月再用、在另一条链上用。防重放的三件套是:一次性随机数(nonce)、域名绑定、时间窗口。
全文分三层:
- 通俗理解:什么是重放攻击
- 实际用处:SIWE、跨链重放事件、API 签名
- 原理与工程要点:SIWE 消息逐行解读、服务端校验清单、EIP-155
第一层:通俗理解
1.1 重放攻击
你给门卫出示一张签了名的条子:"请让我进去。"门卫验了签名,放行。
问题是:如果有人在旁边复印了这张条子,明天拿来再出示一次,门卫一验,签名还是真的,于是也放行了。签名本身没被伪造,只是被重复使用了。这就是重放攻击。
1.2 怎么防
在条子上多写几样东西:
| 写上 | 效果 |
|---|---|
| "今天门卫给我的暗号是 7341" | 每次的暗号都不同,旧条子作废(nonce) |
| "给 A 公司的门卫" | 拿去 B 公司没用(域名绑定) |
| "下午 3:00 到 3:10 有效" | 过期作废(时间窗口) |
第二层:实际用处
- Sign-In with Ethereum(EIP-4361):用以太坊地址登录网站的标准格式,钱包会把关键字段(尤其是域名)解析出来醒目显示,域名对不上时会警告。
- 以太坊与以太坊经典的分叉(2016):分叉后两条链的交易格式相同,一笔在 ETH 上签的交易可以被原样拿到 ETC 上再执行一次,导致不少人在另一条链上"莫名其妙"转出了币。后来的 EIP-155 把 chainId 写进交易签名,堵住了这个漏洞。
- 云服务 API 签名:AWS 的请求签名(SigV4)把时间戳、请求路径、服务区域都签进去,超过几分钟的请求直接拒绝。
- 支付接口:支付回调通常带订单号和时间戳,服务端记录已处理的订单号,防止同一笔回调被重复入账。
Caloogy Wallet 也采用了 SIWE 绑定地址,并要求签名只能使用一次。
第三层:原理与工程要点
3.1 一条 SIWE 消息长什么样
example.com wants you to sign in with your Ethereum account:
0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed
Bind this address to your account.
URI: https://example.com
Version: 1
Chain ID: 1
Nonce: 8f3k2QpZ7mXw
Issued At: 2026-11-05T10:00:00Z
Expiration Time: 2026-11-05T10:05:00Z
| 字段 | 防什么 |
|---|---|
| 第一行的域名 | 钓鱼站诱导你签"给 example.com 的消息":钱包会对比当前网站和消息里的域名 |
| 地址 | 签名必须恢复出这个地址 |
| Chain ID | 跨链重放 |
| Nonce | 同一签名被用第二次 |
| Issued At / Expiration Time | 很久以前截获的签名被拿来用 |
3.2 服务端校验清单
- nonce 必须由服务器生成并记录,绑定到当前会话,用过立即作废;
- 消息里的域名、URI 必须是自己的;
- 当前时间在
Issued At和Expiration Time之间; - 用
ecrecover从签名恢复出地址,必须和消息里的地址一致; - 智能合约钱包(如 Safe)没有私钥,要改用 EIP-1271 调用合约的
isValidSignature验证。
第 1 条最容易做错:如果 nonce 由客户端生成,或者服务端不记录"用过的 nonce",防重放就形同虚设。
3.3 EIP-155:把链 ID 签进交易
EIP-155 之前,交易签名的内容是 (nonce, gasPrice, gasLimit, to, value, data),里面没有任何"这是哪条链"的信息。EIP-155 之后变成
签名里的 值也编码了 chainId。现在主流钱包都强制使用 EIP-155,EIP-712 的结构化签名也有自己的 domain(含 chainId 和合约地址),见《盲签与所见即所签》。
3.4 只签你看得懂的东西
签名登录安全的另一个前提,是钱包能把消息解析并展示出来。如果网站让你签一串看不懂的十六进制(eth_sign 签裸哈希),那它可能根本不是登录消息,而是一笔交易的哈希。所以正确的实现要求:登录只用 personal_sign 签可读文本,从不使用 eth_sign。
总结
| 层次 | 要点 |
|---|---|
| 直觉 | 真签名也会被复印再用,要写上"给谁、何时、仅一次" |
| 三件套 | nonce(服务器发、用完作废)、域名、时间窗口 |
| 链上 | EIP-155 把 chainId 签进交易,终结跨链重放 |
| 底线 | 只签可读、可解析的消息 |