7:用户数据签名详解(加解密、签名系列)

  • • 发表于 8年前
  • • 作者 Roluce
  • • 7526 人浏览
  • • 2 条评论
  • • 最后编辑时间 8年前
  • • 来自 [技 术]

原创声明:本文为作者原创,未经允许不得转载,经授权转载需注明作者和出处

1:为什么要做签名


由上图可知:有三个传输过程:

1:微信服务器A与小程序B之间的数据传输
2:小程序B与自己服务器C之间的数据传输
3:微信服务器A与自己服务器C之间的数据传输


破坏数据完整性的原因:

正常来说,数据从A传输到B,再到C,ABC三点的数据都是一样的。
但也许是不一致的,比如:
1:http传输过程中因为软件或硬件问题造成数据丢失一部分。
2:黑客拦截数据、修改数据、继续发送数据。
3:等等等


数据签名的原因:

如果不做任何措施,从B发出数据data,传输过程中“丢失部分数据”或”被黑客修改数据”,然后C接收到数据data2

在C不知道接收的数据data2是否和B发出的数据data是否一致,
如果把不一致的数据当做正确的数据继续操作,将会出现各种安全性的问题。

为了让C知道:接收的数据是否和B发出的数据是否一致,所以采取了签名的机制。


数据完整、安全的重要性:
比如:你去银行取钱,营业员从“远程服务器”查询你的“账户余额”。
“远程服务器”发送过来1000元,你让“同伙”劫持数据,把这1000元改成5000元,并发送给营业员。如果此时没有做数据完整性校验,营业员收到你的余额就是5000元,他就给你5000元。本来你余额为1000元,结果给你5000元。
这仅仅是一个例子,在不同的应用场合,如果不做数据完整性校验,会出现各种问题。
所以,数据的安全、完整性很重要,所以敏感的数据必须做签名验证。


2:有签名的数据传输与判定

现以上图中BC之间传输为例,说明具体数据传输过程:

一:BC先约定好三点:
1:“签名算法”
2:“密钥”(一般是随机生成复杂的字符串)
3:“算法参数的组装规则”(简称’参规’)

(为了防止密钥的泄露,此密钥仅用于本地,不能在网络传输,以防劫持)

二:在B执行:签名算法( 参规(原始数据,密钥) )=签名
三:从B发出“原始数据”和“签名”到C
四:网络传输过程(安全的或者数据丢失或者黑客劫持)
五:C接收到“待定数据”和“签名”
六:在C执行:签名算法( 参规(待定数据,密钥) )=新签名

判定:
1:如果接收的签名=新签名,就说明数据完整、安全(待定数据=原始数据)
2:如果接收的签名!=新签名,说明待定数据异常、不安全(待定数据!=原始数据)


由上边可知:小程序和开发者的三点约定为:
1:签名算法为:sha1
2:密钥为:session_key
3:算法参数的组装规则:“原始数据字符串”连接“密钥字符串”
(注:小程序用的规则如上,比较简单。还可以随意约定规则,比如:数据+密钥+*#abc等)

所以小程序生成签名的公式:
sha1(原始数据+session_key)=signutrue
注:官方文档中的rawData指的就是“原始数据”,raw英文为“原始的,未被加工的意思”


数据从B发送到C的全部情况(四种):

1:传输过程中,一切正常。

在B:签名算法(数据+密钥)=签名,发出“数据”、”签名”
传输过程:”数据”没变,“签名”没变
在C:签名算法(数据+密钥)=签名
结果:签名==签名,可以判定数据安全

2:传输过程中,黑客修改了“数据”

在B:签名算法(数据+密钥)=签名,发出“数据”、”签名”
传输过程:黑客修改了”数据”变为”数据2”,“签名”没变
在C:签名算法(数据2+密钥)=签名2
结果:签名!=签名2,可以判定数据不安全

3:传输过程中,黑客修改了“密钥”

在B:签名算法(数据+密钥)=签名,发出“数据”、”签名”
传输过程:黑客修改了”签名”变为”签名2”,“数据”没变
在C:签名算法(数据+密钥)=签名
结果:签名2!=签名,可以判定数据不安全

4:传输过程中,黑客修改了“数据和密钥”

在B:签名算法(数据+密钥)=签名,发出“数据”、”签名”
传输过程:黑客修改了”数据和签名”变为”数据2和签名2”
在C:签名算法(数据2+密钥)=签名3
结果:签名2和签名3有几率相等,什么情况下相等呢?

因为在C,签名算法(数据2+密钥)=签名3
只有黑客获取密钥,在传输过程中也执行,签名算法(数据2+密钥)=签名2
签名2才等于签名3。
因为为了防止密钥的泄露,此密钥仅用于本地,不在网络传输,所以黑客无法获取密钥。
结果:签名2!=签名3,可以判定数据不安全。


3:signature签名仅检验”基本信息”的完整性


B1的签名相关数据为:
1:rawData
2:signature


通过console.log()打印的rawData的数据为:

"{"nickName":"三石","gender":1,"language":"en","city":"Liaocheng","province":"Shandong","country":"CN", "avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJX1ic14jrA6pbgCw2r8PxlmOtAr5zlvE4icfvwRvelsFAya13PscYatzERE29wia7A1R53fy3QwJxgg/0"}"

结论1:(由rawData的数据信息)

rawData的数据仅包含基本信息的数据,userInfo内的信息永远是保持一致的。
只是格式不一样,一个是JSON字符串,一个是对象。

结论2:(由rawData的数据信息)

在微信服务器签名signature的生成过程为:
signature=sha1(rawData,session_key)
所以signature只能保证基本信息的数据完整性和安全性。

结论3:(由rawData的数据信息)

**encryptedData与iv也需要单独的检验数据完整性
具体方法见此文:
8:用户敏感信息完整性检验(加解密、签名系列)


4:“用户基本信息”的数据签名校验

通常是在自己服务器后台做验证通常是在自己服务器后台做验证

微信服务器端的响应:

小程序调用wx.getUserInfo()时,微信服务器
通过原始数据,session_key生成签名发送给小程序。
即:sha1( 原始数据+ session_key )=signature


开发者的操作为:

1:先获取session_key(传送门)
2:把小程序端调用wx.getUserInfo()获取数据中的 rawData(原始数据)、signature,用wx.request()发送到自己的服务器。
3:在服务器后台,接收传送来的rawData(原始数据)、signature。使用相同的算法计算出签名signature2
即:sha1( rawData+ session_key )=signature2
结果:
比对 signature 与 signature2 即可校验数据的完整性。


算法参数的组装规则的例子:

由官方文档知道:
sha1( rawData+ session_key )=signature
即sha1的参数为:rawData字符串“连接”sesson_key字符串。

如wx.getUserInfo的数据校验:
接口返回的rawData:

{ "nickName": "Band", "gender": 1, "language": "zh_CN", "city": "Guangzhou", "province": "Guangdong", "country": "CN", "avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0" }


用户的 session-key:
HyVFkGl5F5OQWJZZaNzBBg==
所以,用于签名的字符串为:

{ "nickName": "Band", "gender": 1, "language": "zh_CN", "city": "Guangzhou", "province": "Guangdong", "country": "CN", "avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0" }HyVFkGl5F5OQWJZZaNzBBg==

使用sha1得到的结果为:
75e81ceda165f4ffa64f4068af58c64b8f54b88c


5:全部代码实现(PHP后台版)

0:test.wxml(小程序)

 <button bindtap="tap">tap</button>

1:test.js(小程序)

Page({

  tap: function () {

    //调用登录接口
    wx.login({
      success: function (login) {

        //成功,返回登录凭证js_code
        var js_code = login.code;

        //调用获取用户信息接口
        wx.getUserInfo({
          success: function (res) {

            //获取原始数据 
            var rawData = res.rawData;
            //获取签名
            var signature = res.signature;

            //调用网络请求接口,把数据发送给服务器
            wx.request({
              url: 'http://www.yoururl.com/test.php',
              data: {
                js_code: js_code,       //js_code用户获取session_key
                rawdata: rawData,      //原始数据
                signature: signature,  //签名
              },
              method: 'GET', //用Get的请求方式
              header: {
                'content-type': 'application/json'
              },
              success: function (data) {

                //成功,返回signature,signature2数据
                console.log(data);
              }
            })
          }
        })
      }
    });
  }

})

2:PHP后台实现代码

<?php

 /*1:获取session_key********************************/

    //AppID(小程序ID)
    $APPID= "wx138cb91a9900af75"; 

    //AppSecret(小程序密钥)
    $SECRET = "0d082ea09511df65ef3f2300fdbe0147"; 

    //接收小程序端发来的js_code
    $js_code = $_GET[js_code];

    //小程序接口(appid+secret+js_code=sessionId+openId)
    $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$APPID}&secret={$SECRET}&js_code={$js_code}&grant_type=authorization_code";


    //模拟网页的GET请求
    $timeout = 5; 
    $ch = curl_init();                              //初始化curl   
    curl_setopt($ch, CURLOPT_URL, $url);            //设置访问的url地址   
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    //参数为1表示传输数据,为0表示直接输出显示。 
    curl_setopt($ch, CURLOPT_HEADER, 0);            //参数为0表示不带头文件,为1表示带头文件  

    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);   //获取https需要加上此
     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);   //获取https需要加上此

    $json_data = curl_exec($ch);   //执行命令并把获取的数据赋值给$output
    curl_close($ch);            //关闭URL请求   


    //返回的Json数据转换成php对象 
    $obj = json_decode($json_data);

    //获得session_key
    $session_key =$obj->session_key;  

 /*2:校验数据有效性********************************/

      //获取小程序发送来的签名signature
     $signature = $_GET['signature']; 

     //获取小程序发送来的原始数据rawData
     $rawdata = $_GET['rawdata'];    

    //生成signature2
    $signature2 = sha1($rawdata.$session_key);

    //生成键值对数组,用于转换为Json数组发送给小程序
    $arr = ["signature"=>$signature,"signature2"=>$signature2];

    //键值对数组转换为json数组
    $arr_json = json_encode($arr);

    //发送给小程序,在wx.request()的sucess方法接收
    echo $arr_json;



?>
分享到:
2条评论
Ctrl+Enter
作者

Roluce

Roluce

APP:0 帖子:50 回复:112 积分:3610

已加入社区[2933]天

山东_聊城_qq:635068

作者详情》
Top