原创声明:本文为作者原创,未经允许不得转载,经授权转载需注明作者和出处
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元。
这仅仅是一个例子,在不同的应用场合,如果不做数据完整性校验,会出现各种问题。
所以,数据的安全、完整性很重要,所以敏感的数据必须做签名验证。
上图中
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:签名算法(数据+密钥)=签名
结果:签名==签名,可以判定数据安全
在B:签名算法(数据+密钥)=签名,发出“数据”、”签名”
传输过程:黑客修改了”数据”变为”数据2”,“签名”没变
在C:签名算法(数据2+密钥)=签名2
结果:签名!=签名2,可以判定数据不安全
在B:签名算法(数据+密钥)=签名,发出“数据”、”签名”
传输过程:黑客修改了”签名”变为”签名2”,“数据”没变
在C:签名算法(数据+密钥)=签名
结果:签名2!=签名,可以判定数据不安全
在B:签名算法(数据+密钥)=签名,发出“数据”、”签名”
传输过程:黑客修改了”数据和签名”变为”数据2和签名2”
在C:签名算法(数据2+密钥)=签名3
结果:签名2和签名3有几率相等,什么情况下相等呢?
因为在C,签名算法(数据2+密钥)=签名3
只有黑客获取密钥,在传输过程中也执行,签名算法(数据2+密钥)=签名2
签名2才等于签名3。
因为为了防止密钥的泄露,此密钥仅用于本地,不在网络传输,所以黑客无法获取密钥。
结果:签名2!=签名3,可以判定数据不安全。
B1的签名相关数据为:
1:rawData
2:signature
"{"nickName":"三石","gender":1,"language":"en","city":"Liaocheng","province":"Shandong","country":"CN",
"avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJX1ic14jrA6pbgCw2r8PxlmOtAr5zlvE4icfvwRvelsFAya13PscYatzERE29wia7A1R53fy3QwJxgg/0"}"
rawData的数据仅包含基本信息的数据,userInfo内的信息永远是保持一致的。
只是格式不一样,一个是JSON字符串,一个是对象。
在微信服务器签名signature的生成过程为:
signature=sha1(rawData,session_key)
所以signature只能保证基本信息的数据完整性和安全性。
**encryptedData与iv也需要单独的检验数据完整性
具体方法见此文:
8:用户敏感信息完整性检验(加解密、签名系列)
通常是在自己服务器后台做验证通常是在自己服务器后台做验证
小程序调用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
<button bindtap="tap">tap</button>
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);
}
})
}
})
}
});
}
})
<?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;
?>