微信小程序之用户数据解密(七)
【未经作者本人同意,请勿以任何形式转载】
经常看到有点的小伙伴在群里问小程序用户数据解密流程,所以打算写一篇关于小程序用户敏感数据解密教程;
加密过程微信服务器完成,解密过程在小程序和自身服务器完成,即由 encryptData 得到如下数据:
{
"openId": "OPENID",
"nickName": "NICKNAME",
"gender": GENDER,
"city": "CITY",
"province": "PROVINCE",
"country": "COUNTRY",
"avatarUrl": "AVATARURL",
"unionId": "UNIONID",
"watermark":
{
"appid":"APPID",
"timestamp":TIMESTAMP
}
}
准备知识:
- Base64编解码
- AES算法、填充模式、偏移向量
- session_key会话密钥,以及怎么存储和获取
以上3点对于理解解密流程非常重要。
根据官方文档,我梳理了大致的解密流程,如下:
- 小程序客户端调用wx.login,回调里面包含js_code。
- 然后将js_code发送到服务器A(开发者服务器),服务器A向微信服务器发起请求附带js_code、appId、secretkey和grant_type参数,以换取用户的openid和session_key(会话密钥)。
- 服务器A拿到session_key后,生成一个随机数我们叫3rd_session,以3rdSessionId为key,以session_key + openid为value缓存到redis或memcached中;因为微信团队不建议直接将session_key在网络上传输,由开发者自行生成唯一键与session_key关联。其作用是:
- 将3rdSessionId返回给客户端,维护小程序登录态。
- 通过3rdSessionId找到用户session_key和openid。
- 客户端拿到3rdSessionId后缓存到storage,
- 通过wx.getUserIinfo可以获取到用户敏感数据encryptedData 。
- 客户端将encryptedData、3rdSessionId和偏移量一起发送到服务器A
- 服务器A根据3rdSessionId从缓存中获取session_key
- 在服务器A使用AES解密encryptedData,从而实现用户敏感数据解密
重点在6、7、8三个环节。
AES解密三个参数:
- 密文 encryptedData
- 密钥 aesKey
- 偏移向量 iv
服务端解密流程:
- 密文和偏移向量由客户端发送给服务端,对这两个参数在服务端进行Base64_decode解编码操作。
- 根据3rdSessionId从缓存中获取session_key,对session_key进行Base64_decode可以得到aesKey,aes密钥。
- 调用aes解密方法,算法为 AES-128-CBC,数据采用PKCS#7填充。
下面结合小程序实例说明解密流程:
- 微信登录,获取用户信息
var that = this;
wx.login({
success: function (res) {
//微信js_code
that.setData({wxcode: res.code});
//获取用户信息
wx.getUserInfo({
success: function (res) {
//获取用户敏感数据密文和偏移向量
that.setData({encryptedData: res.encryptedData})
that.setData({iv: res.iv})
}
})
}
})
- 使用code换取3rdSessionId
var httpclient = require('../../utils/httpclient.js')
VAR that = this
//httpclient.req(url, data, method, success, fail)
httpclient.req(
'http://localhost:8090/wxappservice/api/v1/wx/getSession',
{
apiName: 'WX_CODE',
code: this.data.wxcode
},
'GET',
function(result){
var thirdSessionId = result.data.data.sessionId;
that.setData({thirdSessionId: thirdSessionId})
//将thirdSessionId放入小程序缓存
wx.setStorageSync('thirdSessionId', thirdSessionId)
},
function(result){
console.log(result)
}
);
- 发起解密请求
//httpclient.req(url, data, method, success, fail)
httpclient.req(
'http://localhost:8090/wxappservice/api/v1/wx/decodeUserInfo',
{
apiName: 'WX_DECODE_USERINFO',
encryptedData: this.data.encryptedData,
iv: this.data.iv,
sessionId: wx.getStorageSync('thirdSessionId')
},
'GET',
function(result){
//解密后的数据
console.log(result.data)
},
function(result){
console.log(result)
}
);
- 服务端解密实现(java)
/**
* 解密用户敏感数据
* @param encryptedData 明文
* @param iv 加密算法的初始向量
* @param sessionId 会话ID
* @return
*/
@Api(name = ApiConstant.WX_DECODE_USERINFO)
@RequestMapping(value = "/api/v1/wx/decodeUserInfo", method = RequestMethod.GET, produces = "application/json")
public Map<String,Object> decodeUserInfo(@RequestParam(required = true,value = "encryptedData")String encryptedData,
@RequestParam(required = true,value = "iv")String iv,
@RequestParam(required = true,value = "sessionId")String sessionId){
//从缓存中获取session_key
Object wxSessionObj = redisUtil.get(sessionId);
if(null == wxSessionObj){
return rtnParam(40008, null);
}
String wxSessionStr = (String)wxSessionObj;
String sessionKey = wxSessionStr.split("#")[0];
try {
AES aes = new AES();
byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
if(null != resultByte && resultByte.length > 0){
String userInfo = new String(resultByte, "UTF-8");
return rtnParam(0, userInfo);
}
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return rtnParam(50021, null);
}
- AES解密核心代码
public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
initialize();
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Key sKeySpec = new SecretKeySpec(keyByte, "AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
byte[] result = cipher.doFinal(content);
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
最后的效果如下:
如果你的小程序没有绑定微信开放平台,解密的数据中不包含unionid参数
小程序绑定微信开放平台连接
总结
从解密的数据看,算得上敏感的数据只有appid;个人觉得openid不是敏感数据,每个用户针对每个公众号会产生一个安全的openid;openid只有在appid的作用域下可用。除非你的appid也泄露了。
那么可以从解密数据得到appid,微信小程序团队是何用意呢?还是前面那句话,openid脱离了appid就什么都不是,openid和appid一起为了方便小程序开发者做到不同小程序应用之间用户区分和隔离,同时能够将微信用户体系与第三方业务体系结合。
所以我认为敏感数据解密的主要用处不是解密后回传给客户端,而是在服务端将微信用户信息融入到自身业务当中。
详细学习请参考:
小程序数据解密:https://github.com/cocoli/weixin_smallexe/tree/master/chaptor_05
java解密实现:https://github.com/cocoli/springboot-weapp-demo
相关讨论专题:http://www.wxappclub.com/topic/429
你也可以关注我的微信公众号『ITNotes』, 一起交流学习 。
微信小程序之用户数据解密(七)的更多相关文章
- 干货,看微信小程序后台用户数据如何演变和递增
这几天发现附近小程序又多了好几家,其中有普通小程序和门店小程序,把它们做一个对比,门店小程序更多的像一张名片,只有基本的企业名称.地址.营业时间.电话和门店照片,和普通小程序相比显得逊色许多.楼下的水 ...
- 微信小程序 获取用户信息 encryptData解密 C#版本
最近学习小程序开发,需要对encryptData解密,获取用户信息,官方源码没有C#版本,网上的资料比较杂,有的使用还有问题,下面贴一下自己亲试可以使用的一个源码 1.code 换取 session_ ...
- [微信小程序] 微信小程序获取用户定位信息并加载对应城市信息,wx.getLocation,腾讯地图小程序api,微信小程序经纬度逆解析地理信息
因为需要在小程序加个定位并加载对应城市信息 然而小程序自带api目前只能获取经纬度不能逆解析,虽然自己解析方式,但是同时也要调用地图,难道用户每次进小程序还要强行打开地图选择地址才定位吗?多麻烦也不利 ...
- 基于微信小程序的用户列表点赞功能
代码地址如下:http://www.demodashi.com/demo/13997.html 一.前言 (1).适合人群 1.微信小程序开发者 2.前端工程师 3.想入门学习小程序开发的人员 4.想 ...
- Laravel wxxcx 微信小程序获取用户信息
wxxcx 是Laravel5微信小程序登录获取用户信息扩展 部署 12345678 # 安装$ composer require iwanli/wxxcx# 注册服务# 在 /config/app. ...
- 微信小程序的ajax数据请求wx.request
微信小程序的ajax数据请求,很多同学找不到api在哪个位置,这里单独把小程序的ajax请求给列出来,微信小程序的请求就是wx.request这个api,wx.request(一些对象参数),微信小程 ...
- 微信小程序从零开始开发步骤(七)引入外部js 文件
上一章讲到小程序页面的四种常见的跳转的方法,这一章写如何引入一个外部的js文件,既utils文件夹的用处,其实步骤很简单: 1:准备好外部想要引入的外部文件,命名为util.js,并且填充固定的文件内 ...
- 微信小程序 获取用户信息并保存登录状态
微信小程序 获取用户信息并保存登录状态:http://www.360doc.com/content/18/0124/11/9200790_724662071.shtml
- 微信小程序获取用户信息,解密encryptedData 包括敏感数据在内的完整用户信息的加密数据
package com.iups.wx.wxservice; import java.io.UnsupportedEncodingException; import java.security.Alg ...
随机推荐
- 线性判别分析LDA原理总结
在主成分分析(PCA)原理总结中,我们对降维算法PCA做了总结.这里我们就对另外一种经典的降维方法线性判别分析(Linear Discriminant Analysis, 以下简称LDA)做一个总结. ...
- 黑云压城城欲摧 - 2016年iOS公开可利用漏洞总结
黑云压城城欲摧 - 2016年iOS公开可利用漏洞总结 作者:蒸米,耀刺,黑雪 @ Team OverSky 0x00 序 iOS的安全性远比大家的想象中脆弱,除了没有公开的漏洞以外,还有很多已经公开 ...
- Hyper-V3:虚拟机的配置
在Hyper-V成功新建一台虚拟机,在正式使用之前,必须配置VM使用的硬件资源,并授予用户访问VM的权限等,本文罗列出一些常见的配置,供读者参阅. 一,为虚拟机分配使用的内存 在Hyper-V Man ...
- document.documentElement.clientHeight 与 document.body.clientHeight(杜绝千篇一律的抄袭!!)
document.documentElement.clientHeight 与 document.body.clientHeight用来获取页面可视高度我觉得有点问题.这两个应该不是一个东西. 页面中 ...
- 9、 Struts2验证(声明式验证、自定义验证器)
1. 什么是Struts2 验证器 一个健壮的 web 应用程序必须确保用户输入是合法.有效的. Struts2 的输入验证 基于 XWork Validation Framework 的声明式验证: ...
- 挑子学习笔记:特征选择——基于假设检验的Filter方法
转载请标明出处: http://www.cnblogs.com/tiaozistudy/p/hypothesis_testing_based_feature_selection.html Filter ...
- Consul-template的简单应用:配置中心,服务发现与健康监测
简介 Consul-template是Consul的一个方扩展工具,通过监听Consul中的数据可以动态修改一些配置文件,大家比较热衷于应用在Nginx,HAProxy上动态配置健康状态下的客户端反向 ...
- 初识JavaScript
JavaScript ECMA-262: 变量,函数,对象,数据类型....唯独没有输入和输出. Javascript:包含 ECMA-262,核心 BOM 浏览器对象模型, DOM 文档对象模型 什 ...
- 微信小程序体验(1):携程酒店机票火车票
在 12 月 28 日微信公开课上,张小龙对微信小程序的形态进行了阐释,小程序有四个特定:无需安装.触手可及.用完即走.无需卸载. 由于携程这种订酒店.火车票和机票等工具性质非常强的服务,非常符合张小 ...
- linux字符串url编码与解码
编码的两种方式 echo '手机' | tr -d '\n' | xxd -plain | sed 's/\(..\)/%\1/g' echo '手机' |tr -d '\n' |od -An -tx ...