Sign-In with Ethereum让用户用钱包签名建立网站会话,但它不是“签名正确就登录”。服务端必须确认消息是自己刚刚发起、属于当前域名和URI、仍在有效期内,并且签名账户与消息地址一致。
一条SIWE消息应能被人读懂
标准消息以domain要求某个address登录开头,可包含statement,随后列出URI、Version、Chain ID、Nonce和Issued At;Expiration Time、Not Before、Request ID、Resources等字段按需加入。钱包应展示完整域名和statement,不能只显示“Sign message”。
服务端验证顺序
- 从当前登录会话取出服务端生成的nonce,要求与消息完全一致且尚未使用。
- 检查domain等于实际站点允许域,URI属于预期来源,防止钓鱼页面借用签名。
- 验证version与chain ID,确认应用是否允许该链。
- 检查issued-at、expiration-time和not-before,处理时钟偏差但不无限放宽。
- 按标准消息字节验证签名,恢复地址并与消息address比较。
- 原子消费nonce,再创建绑定address、domain和权限范围的短期会话。
| 失败场景 | 正确响应 |
|---|---|
| nonce已用或不存在 | 拒绝并发新挑战,不重复接受旧签名 |
| domain不匹配 | 直接拒绝,不用CORS或跳转修正 |
| 消息已过期 | 要求重新签名 |
| 合约账户无法ecrecover | 按目标链调用ERC-1271 |
nonce要一次性且不可预测
nonce由服务端生成并与浏览器会话绑定,长度和随机性应足以防猜测。不要把裸时间戳、钱包地址或递增数字当作唯一nonce;若设计包含近期时间戳,也必须再加入足够随机且由服务端校验的一次性成分。不能把同一nonce同时发给多个登录请求。验证与消费应在一个事务中完成,避免两个并发请求重复使用。
登录签名不能变成资产授权
SIWE消息是文本登录声明,不应混入approve、Permit或交易calldata。产品若在登录后要求其他签名,要分开展示用途。用户看到陌生domain、长期expiration或模糊statement时应取消。
会话仍需要传统Web安全
钱包签名只证明登录时控制地址,不自动防止会话cookie被盗。服务端仍需使用Secure、HttpOnly、SameSite策略,轮换会话,处理登出与权限变更,并限制Resources声明的访问范围。
EOAs与合约账户验签路径不同
EOA可通过secp256k1签名恢复地址;智能合约钱包没有可恢复的单一私钥,服务端应在消息指定的chain ID上调用ERC-1271 isValidSignature,并核对magic value。若只尝试ecrecover,Safe等账户会被错误拒绝;若在错误链调用,同一地址也可能代表不同代码。
验证日志应保存消息哈希、domain、nonce状态、地址、链和失败类别,但不要长期记录完整IP或可重放会话令牌。安全审计还应测试大小写域名、代理头伪造、并发消费nonce、过期边界和合约钱包升级后的行为。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。