在尝试编写服务端验证sign的逻辑时遇到了一个问题,我在阅读了您所撰写的sign校验规则后发现,您将"毫秒时间戳+换行符+密钥"通过HmacSHA256进行计算。
但HmacSHA256本身是不可逆的计算,意味着服务端无法解密客户端所提供的签名来获取时间戳进行校验。
我想您的设计本意应该是服务端使用相同的密钥和时间戳重新计算签名并与客户端的计算结果对比,但问题出在服务端无从得知客户端计算签名时所用的时间戳。
另外,此种方式也无法规避重放攻击,您在文档中所写的"与请求调用时间误差不能超过1小时"实际上允许一小时内的任意签名都可被无限次使用。
在此,我向您推荐一种可行的签名方式,以此确保请求来自于可信的客户端。
// Node.js
const crypto = require("crypto");
let secret = "This is secret"; // 密钥
let hmac = crypto.createHmac("sha256", secret);
let from = "+8618001234567"; // 来源手机号
let content = "Hello world."; // 推送的消息主体
let random = Math.random().toString(); // 随机数 用于规避重放攻击
let timestamp = (new Date).getTime(); // 毫秒时间戳
// 用于签名的字符串使用 时间戳 + 来源手机号 + 消息主体 + 随机数 的格式
// 这样可以在确保请求中的任何内容在到达服务端之前没有被篡改的同时避免重放攻击
let stringToSign = timestamp + from + content + random;
// 示例: "1614366886029+8618001234567Hello world.0.11362871958842535"
let signature = hmac.update(stringToSign) // 签名字符串
.digest() // 获取签名结果
.toString('base64'); // 转为base64
// 最后,将random、timestamp连同消息主体一起发送给服务端。
// 服务端使用相同的算法验证签名后,使random在一段时间内不能被再次使用
// 如规定timestamp误差不能超过15分钟,则相同的random在以服务器时间为基准的前、后15分钟均无效
// 实际实践中,只需缓存random并在15分钟后删除即可
let dataToPost = {
from: from,
content: content,
timestamp: timestamp,
random: random,
sign: signature
}
console.log(dataToPost); // 输出