SIWE登录签名如何安全验证? 图 1
SIWE登录签名如何安全验证? · 图 1

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”。

服务端验证顺序

  1. 从当前登录会话取出服务端生成的nonce,要求与消息完全一致且尚未使用。
  2. 检查domain等于实际站点允许域,URI属于预期来源,防止钓鱼页面借用签名。
  3. 验证version与chain ID,确认应用是否允许该链。
  4. 检查issued-at、expiration-time和not-before,处理时钟偏差但不无限放宽。
  5. 按标准消息字节验证签名,恢复地址并与消息address比较。
  6. 原子消费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、过期边界和合约钱包升级后的行为。

EIP-712授权核对见结构化签名,合约账户验签见ERC-1271,连接未知站点前可参考智能合约连接检查