基于Hash摘要签名的公网URL签名验证设计方案

基于Hash摘要签名的公网URL签名验证设计与约定

为什么要对url参数签名

在特定的环境下存在A系统页面打开B系统(不是同一个公司)的需求,由于B系统中不存在用户登陆,所以必须保证A通过URL调用带给B的参数是可信且未经篡改的。比如A的用户需要打开B的页面查看待审批充值订单url https://admin.isexample.com/admin/abc?isid=10000011 由于这个url是在用户浏览器上可以修改的,为了避免用户在浏览器上将isid改为其他值(比如https://admin.isexample.com/admin/abc?isid=10000012 ),这会导致系统的用户信息泄露,为了避免这种情况产生,我们需要对url中的参数进行签名。

有哪些关注点

  1. url参数放篡改
  2. url参数仿复用、仿重放攻击(url有有效期,生成时间与访问时间间隔太久会阻止访问)
  3. 验证通过后需要重定向一次 防止用户刷新原来的页面导致一次性token再次使用而系统返回错误,还需要一个随机字符串来防止每次的提交可能相同,即除了时间戳还需要一个随机字符串nonce_str
  4. 需要统一时区(比如以0时区为标准)
  5. 私下约定的加密字符串更换成本(使用两个?)
  6. Get请求url太长是否影响、大小写是否影响
  7. 通用性,需要考虑出来当前网页url验证外,还可能在系统其他部分使用(可能被作>统一拦截)
  8. 签名认证的结果是否只对当前页面有效,其他页面是否可以跳过认证(不安全,认证应该是一次性的,每次不同系统的交互都应该重新认证鉴权,如何保证重定向时认证息的传递,页面刷新呢?)
  9. 加密时,需要先做签名再做urlencode,解密时需要先urldecode,再签名对比
  10. url传递的参数值不支持数组形式 比如 a=1&a=2&a=3 是不被支持的

基本方法流程(部分参考微信支付签名

  1. 第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

* 特别注意以下重要规则: *
- 参数名ASCII码从小到大排序(字典序);
- 如果参数的值为空不参与签名;
- 参数名区分大小写;
- 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
- 微信接口可能增加字段,验证签名时必须支持增加的扩展字段

  1. 第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

    • key设置路径:IS和JV线下约定私钥字符串 不超过32位

举例

假设传送的参数如下:

appid: 1

timestamp: 1505811040085

nonce_str: edd4fb6c-38a0-4928-be04-4cd899f19580

AID: 1000011

serialID: 100010103836

第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:

stringA=”AID=1000011&appid=1&nonce_str=edd4fb6c-38a0-4928-be04-4cd899f19580&serialID=100010103836&timestamp=1505811040085”;

第二步:拼接API**:

stringSignTemp=stringA+”&key=58fcd0326a1b94f0ef2c33236fff5b2b” //注:key为**key
sign=MD5(stringSignTemp).toUpperCase()=”9A0A8659F005D6984697E2CA0A9CF3B7” //注:MD5签名方式
sign=hash_hmac(“sha256”,stringSignTemp,key).toUpperCase()
=”6A9AE1657590FD6257D693A078E1C3E4BB6BA4DC30B23E0EE2496E54170DACD6” //注:HMAC-SHA256签名方式

参数部分得到的String为

AID=1000011&appid=1&nonce_str=edd4fb6c-38a0-4928-be04-4cd899f19580&serialID=100010103836&timestamp=1505811040085
&sign=1228D34304D42F59FFDEDBDA66E97034

由于采用get请求的方式,我们需要先对参数进行urlencode 防止出现url中的数据被转义的情况发生

最终得到最终发送的数据:

http://localhost:8080/abc?AID%3D1000011%26appid%3D1%26
100010103836%26timestamp%3D1505811040085%26
sign%3D1228D34304D42F59FFDEDBDA66E97034

2、生成随机数算法
API接口协议中包含字段nonce_str,主要保证签名不可预测。我们推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。

A系统同事需要做的工作(注意事项)

  1. 可以参考demo实现A端的代码
  2. 在所有由A通过网页链接跳转到B系统的处理方法上采用此签名方法,保证传递参数的安全性
  3. 在生成签名的过程中,每一次卖家打开jv页面都应该请求服务器,由服务器生成相应参数并签名
  4. A系统同事应该严格检查用户输入,关键参数不能使用卖家可能修改的数据来作为参数传递给B系统,比如AID不应该从前端用户取,而应该从用户的登陆状态中获取,防止前端卖家修改AID导致信息泄漏

B系统同事需要做的工作(注意事项)

  1. 利用redis缓存所有使用过的sign 并过滤使用过的sign
  2. 校验时间戳,过滤一定时间以前生成的时间戳

可以改进的地方

约定的私钥应该通过api进行动态设置,不应该固化到配置文件,需要做到随时可以更新且更新的时间附近不影响业务

酷炫的效果图

点击生成url生成签名的url
基于Hash摘要签名的公网URL签名验证设计方案
点击url 打开新窗口 会返回验证结果

基于Hash摘要签名的公网URL签名验证设计方案
修改下生成的url 验证结果将失败
基于Hash摘要签名的公网URL签名验证设计方案

git地址下载 https://git.oschina.net/zhangruhong/urlsigndemo.git

基于Hash摘要签名的公网URL签名验证设计方案

参考文档

http://blog.csdn.net/sdutphp/article/details/51991738
https://www.cnblogs.com/lori/p/5336550.html
http://www.cnblogs.com/encode/p/6003136.html
http://blog.csdn.net/starfd/article/details/43487587
http://blog.csdn.net/lplj717/article/details/51828692