移动端调起微信支付时如何防止参数被篡改?
我在做H5页面集成微信JSAPI支付,后端生成prepay_id后返回给前端,但担心中间人篡改timestamp或nonceStr这些参数。试过加签但不知道前端要不要参与验签,文档看得有点晕。
目前后端返回的签名是这样生成的:
const sign = crypto.createHash('md5')
.update(appId=${appId}&nonceStr=${nonceStr}&package=prepay_id=${prepayId}&signType=MD5&timeStamp=${timestamp}&key=${apiKey})
.digest('hex')
.toUpperCase();
前端直接用这个sign调WeixinJSBridge.invoke('getBrandWCPayRequest', ...),但总感觉不安全,是不是应该在前端也做点什么验证?
先说结论:前端不需要额外验签,微信客户端调起支付时会自动校验签名。
问题在于你用的API有点老,现在主流是wx.chooseWXPay,不是直接调WeixinJSBridge。不过核心逻辑是一样的——签名正确的情况下,任何参数被篡改都会导致支付调起失败。
你签名的生成方式有个问题。JSAPI支付签名和公众号jssdk签名是不同的,正确的生成顺序应该是这样:
后端返回给前端的数据要包含这5个参数:appId、timeStamp、nonceStr、package、signType、paySign
然后前端调起时这样用:
wx.chooseWXPay({
timestamp: res.timeStamp,
nonceStr: res.nonceStr,
package: res.package,
signType: res.signType,
paySign: res.paySign,
success: function(res) {
// 支付成功
}
});
微信服务器收到请求后会用package里的prepay_id去校验,同时验证paySign签名。中间人改timestamp或nonceStr都会导致签名不匹配,支付直接失败。
所以你只需要确保后端生成的签名算法没问题就行,前端该传什么传什么,不需要自己再算一遍。
如果想更保险,可以在前端做一下参数合法性检查,比如timestamp是10位数字、nonceStr是32位随机字符串,但这更多是防御性编程,微信那边已经帮你把篡改的路堵死了。
你的做法其实没问题,问题出在理解上。微信支付的签名就是用来防篡改的,只要确保:
1. 签名参数和请求参数完全一致
2. 前端不改动任何参数
建议改成这样:
后端直接返回完整支付参数包,包括sign:
前端直接用这个对象调支付,千万别手贱去改任何一个字段:
重点:前端不要参与签名计算,那是后端的事。微信那边会用同样的算法验签,如果参数被改过支付就会失败。