以太坊数字签名安全疑问:攻击者能否复用签名消息?
关于数字签名复用的安全性疑问解答
首先直接给你答案:是的,攻击者完全可以复用你签署过的同一条消息的签名,多次发送到其他地址或者重复提交给服务端。
为什么会出现这种情况?
数字签名的本质是对特定消息的哈希值进行加密签名——只要消息内容完全一致,生成的签名就是固定不变的。你当前的代码里,只是对纯文本"hello"进行签名,没有添加任何唯一标识或者上下文约束信息。一旦攻击者截获了你的sign和message数据,他们就可以拿着这组数据重复发送,而如果服务端只验证签名和消息的匹配性,根本无法区分这是你第一次发送的请求,还是攻击者复用的请求。
看一下你这段客户端代码的问题:
var msg = "hello"; var sh3 = web3.sha3(msg); web3.eth.sign(account, sh3, (error, result) => { sendToServer({ sign: result, message: msg }); });
这里签名的消息就是固定的"hello",没有任何额外的区分信息,所以签名是完全可复用的。
怎么解决这个问题?
要防止签名被复用,核心思路是让每次要签名的消息都带有唯一且不可重复的上下文信息,这样即使消息主体相同,加上这些信息后整体内容就不一样了,签名也就无法复用。常见的做法有这几种:
- 加入唯一随机数(nonce):每次请求生成一个独一无二的随机字符串(也可以从服务端提前获取),把它加入到要签名的消息里。服务端需要记录所有已经处理过的nonce,收到请求时先检查nonce是否已被使用,用过就直接拒绝。
- 绑定接收地址:把目标服务端或者接收方的以太坊地址也包含到签名消息中,这样这个签名就只能对应这个特定的接收方,拿到其他地址去用就会验证失败。
- 添加时间戳:给消息加上当前时间戳,服务端设置一个有效期(比如5分钟),超过这个时间的签名直接拒绝,限制攻击者的可利用窗口。
修改后的代码示例
这里给你一个结合了nonce、接收地址和时间戳的客户端代码示例:
// 生成唯一nonce(可以用更严谨的随机生成方式,比如UUID) const nonce = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2); // 假设接收服务端对应的地址是recipientAddress const recipientAddress = "0x123..."; // 把上下文信息和原消息拼接成新的待签名内容 const msg = `hello|${recipientAddress}|${nonce}|${Date.now()}`; const sh3 = web3.sha3(msg); web3.eth.sign(account, sh3, (error, result) => { sendToServer({ sign: result, message: msg, nonce: nonce }); });
对应的服务端验证逻辑需要额外做这些检查:
- 从消息中解析出nonce,查询本地存储,确认这个nonce没有被使用过;
- 解析时间戳,检查是否在设定的有效期内;
- 确认消息里的接收地址和当前服务端的地址一致;
- 最后再验证签名和消息哈希的匹配性。
这样就能彻底避免签名被复用的问题啦。
内容的提问来源于stack exchange,提问作者Diagonal Think
相关产品推荐
相关产品推荐

