微信小程序加密网络通道

从基础库 2.17.3 开始支持

一、功能介绍

在前后端开发联调过程中,开发者通常会使用代理软件对自己开发的项目服务做请求转发,便于切换开发环境和进行测试。

代理软件可以在 客户端 与 真实服务器 之间进行数据传输。

这时,代理软件可以解密并查看通过 SSL 加密的数据,然后再重新加密数据发送给原始目的地。对于客户端和服务器来说,它们仍然认为自己处于一个安全的加密连接中,实际上数据的安全性已被破坏。

除了监听之外,由于代理软件可以解密 SSL 加密的通信,因此也支持修改传输中的数据,甚至插入恶意内容。上述这种中间人代理,也会被不法分子恶意使用,用于任意监听自己设备中的所有请求。

他们会在自己的设备中运行其他开发者开发的「微信小程序」、「WEB 网页」 或「原生APP」,然后通过中间代理获取和篡改正常请求的「请求包」和「响应包」,并有针对的对目标请求进行重放,用于实现恶意的目的(比如刷经验、修改游戏结果、创建 1 分钱订单等)。

为了防止中间人代理攻击,我们就需要在 SSL 加密的基础上,对请求包和响应包的明文再进行一次加密,这种方法可以提供额外的安全层次,保障数据即使在被代理软件解密后仍保持安全。

目前微信团队针对加密网络通道提供两种解决方案:

  1. API 自实现方案: 微信平台维护了一个用户维度的可靠 Key,开发者可以分别通过微信小程序前端 API 和微信后台提供的服务端接口获取到,并自己实现对请求包的加密。
  2. 微信网关方案: 微信团队结合多年安全防护经验积累推出的微信网关,具备系统化、多层级的安全防护能力。开发者接入后,就会将「微信小程序」到「开发者服务端」的通信链路自动从公网链路切换到微信专线链路,并且全程对请求内容进行二层加密,有效防止中间人代理攻击。

获取加密 key 信息需要向微信平台发送请求,产生耗时,从而对业务性能产生负担。如开发者希望安全加密的同时减少业务无关的耗时,可以使用微信网关方案。

二、API 自实现方案

为了避免微信小程序与开发者后台通信时数据被截取和篡改,微信平台维护了一个用户维度的可靠 Key,用于微信小程序和后台通信时进行加密和签名。

开发者可以分别通过微信小程序前端 API 和微信后台提供的服务端接口,获取到用户加密 key。

微信平台只提供可靠的加密 key,开发者需自行实现加密方式,对向服务端接口请求的数据进行端处加解密。

在微信小程序中开发者可以使用UserCryptoManager.getLatestUserKey获取获取用户最新的加密密钥信息。

2.1 前端调用示例

const somedata = 'xxxxx'
const userCryptoManager = wx.getUserCryptoManager()
userCryptoManager.getLatestUserKey({
    success({encryptKey, iv, version, expireTime}) {
        const encryptedData = someAESEncryptMethod(encryptKey, iv, somedata)
        wx.request({
           data: encryptedData,
           success(res) {
                const decryptedData = someAESDEcryptMethod(encryptKey, iv, res.data)
                console.log(decryptedData)
           }
        })
    }
})

someAESEncryptMethod 和 someAESDEcryptMethod 分别为加解密函数,由开发者自行引入加解密库来实现,基础库暂时不提供加解密能力。

开发者可参考开源加密库: https://github.com/flash1293/aes-wasm https://github.com/ricmoo/aes-js

根据自身业务情况,寻找合适的加密方式:

  • 非对称加密: 客户端和服务器维护两组公私加密密钥,每次接口请求时,先对明文数据进行加密,另一方收到密文后用对应的反向密钥解密。常见的非对称加密有 RSA、DSA、ECC,非对称加密安全性高,但加解密速度慢,不太适合请求体或响应体太大的情况。
  • 对称加密: 客户端和服务端维护一组密钥,每次接口请求时,对明文数据进行加密,另一方收到时用同样的密钥解密。常见的对称加密有 DES、3DES、IDEA、RC5、RC6、Blowfish,对称加密加解密速度快,适合对大数据做加解密处理,安全性有所下降。
  • 整合方案: 请求时,客户端对明文用对称加密方式加密,对称密钥随机生成,然后用非对称加密方式加密前面的对称密钥,最后将密文和加密后的对称密钥内容合并发出;服务端收到时,按照反向流程进行解密,响应包也是相同的处理流程。

在开发者服务端,可以调用getUserEncryptKey后台接口获取用户最近三次的key。在获取key的同时,接口会携带version信息,开发者可以比较version版本来选择使用对应的key对数据进行加解密。

2.2 服务端调用示例

curl -X POST "https://api.weixin.qq.com/wxa/business/getuserencryptkey?access_token=ACCESS_TOKEN&openid=OPENID&signature=SIGNATURE&sig_method=hmac_sha256"

三、微信网关方案

可以参考此文档接入微信网关:微信小程序一键接入微信网关

安全键盘

从基础库 2.18.0 开始支持

很多微信小程序业务需要输入一些敏感信息,比如密码口令,身份证,手机号等。不专业的做法是使用明文提交到业务后台,在网络传输中非常容易泄漏出去,同时也不满足合规要求。也有一些改进的做法,利用javascript对敏感信息进行加密,比如把明文的密码口令加密成为密文后再提交到业务后台。但因为微信小程序本质是基于H5技术,安全性不高,比如在H5上使用javascript比较容易能看到加密逻辑,或者加密强度不够,第三方输入法监听,内存遍历等,还是会造成口令泄露等问题。

为提高微信开放平台生态安全性,针对微信小程序内数字密码输入场景中可能存在的安全问题,微信侧在input组件开放了安全键盘类型。通过引入安全键盘,微信小程序可以在用户输入过程中对关键信息时进行加密,防止键盘窃听,内存保护,有效保障用户数据资产的安全。

安全键盘保护原理

安全键盘采用非对称加解密算法,该算法需要两个密钥,一个叫公钥,可以公开,另一个叫私钥,需要私密保管。其中公钥加密的密文,只有私钥才能解开,而且通过公钥没办法计算出私钥,这样黑客即使拿到公钥也没办法解密密文。我们一般把公钥放到客户端上(比如微信小程序环境)做加密,私钥放到业务后台,这样就只有后台才能解密。黑客比较容易攻击本地客户端,但是攻破后台则难很多,甚至有些业务把私钥存储到硬件加密机芯片里面,这种情况黑客更是没办法获取得到私钥,因此采用非对称加解密算法是安全键盘推荐的模式,安全性可以得到保障。 为了保证私钥的私密性价值,我们要求不同的微信小程序业务使用自己独有的公钥私钥对,这样就可以完美的做到业务加密的数据隔离,业务A的公钥加密数据,就只有业务A自己的私钥可以解开,业务A的责任就是负责保护好自己的独有私钥即可。 为了证明一个公钥是属于业务A的,我们会颁发一个数字证书给到业务A的开发者,数字证书由腾讯官方签名,保证了可靠性与不可篡改性,数字证书里面会绑定一个业务独有的公钥, 业务的私钥是在向腾讯申请数字证书的过程中产生的,业务负责管理好自己的私钥,这个过程中,腾讯仅能接触到公钥,没办法得到业务自己的私钥,意味着即使是腾讯也没办法去解密微信小程序业务的用户输入的密码口令。为了国家合规的要求,我们颁发的是国产密码算法的数字证书,意味着非对称加解密算法是使用sm2算法,而非rsa等国际算法。

不同微信小程序业务,可能要加密的数据的格式可能有不同的要求,比如当用户输入的口令是“123456”,有些业务直接对明文做加密即可, 有些业务可能想先做一个哈希处理再加密,比如使用md5(“123456”), 做了哈希后,能更有效的保护用户的明文密码,让业务都不好推测用户实际密码是什么, 另一些业务可能采用sha1哈希算法sha1(“123456”), 也有业务采用更合规的国产密码哈希算法sm3(“123456”),甚至有些业务希望加上一些混淆字符(密码学中叫加盐)到口令明文里面,做更好的保护,就可能会变成sm3(“123456+abc”), 这里面”+abc”就是额外加上的混淆字符示例。 所以为了能让不同的微信小程序业务有符合自己业务需求的加密格式,微信小程序安全键盘也开放了密码格式配置的能力,从而更好的跟自己的整体业务结合在一起,但是这个格式做不到万能兼容所有,所以当你应用微信小程序安全键盘的时候,可能会涉及部分后台验密服务的改造工作量,请提前评估可行性。

使用流程

1 生成证书签署请求

开发者可自行生成公钥私钥、证书签署请求,也可通过微信侧提供的工具生成证书签署请求。通过微信侧提供的工具(Windows / Mac)生成证书签署请求的步骤如下:

  1. 通过SM2 Generate Key Pair功能生成公钥私钥

  1. 通过SM Generate Cert CSR功能生成CSR

2 生成证书

在微信小程序管理后台「开发」-「开发管理」-「开发设置」-「安全键盘证书」板块填入CSR进行生成。

3 使用证书

  1. 将生成的证书放入微信小程序代码包中。
  2. 在input组件中设置type=“safe-password”,并设置相关参数(safe-password-cert-path、safe-password-time-stamp、safe-password-length、safe-password-nonce、safe-password-salt、safe-password-custom-hash)。

代码示例

<input 
  style="border: 1px solid blue;"
  type="safe-password"
  placeholder="123456"
  safe-password-cert-path="/minipro_test_cert.crt" 
  safe-password-time-stamp="1618390369" 
  safe-password-nonce="1618390369" 
  safe-password-salt="zefengwang" 
  safe-password-custom-hash="md5(sha1('foo' + sha256(sm3(password + 'bar'))))"
  bind:blur="onBlur"
  bind:input="onInput"
  value="{{value}}"
></input>
<button bind:tap="onClear">clear</button>

<view>{{detail}}</view>
Page({
  data: {
    value: '123'
  },
  onInput(res) {
    console.log('onInput', res)
    this.setData({
      value: res.detail.value,
    })
  },
  onClear() {
    this.setData({
      value: '',
    })
  },
  onConfirm() {
    console.log('confirm')
  },
  onBlur(res) {
    console.log('onBlur', res)
    this.setData({
      detail: JSON.stringify(res.detail, null, 2)
    })
  },
})

密文

密文格式

安全键盘为了保护用户口令,采用了各种密码学算法来保护用户敏感信息,这些算法是可以根据微信小程序业务的实际需要做灵活配置的,因为不同的微信小程序,采用的口令加密格式不同,所以有必要做符合自己业务的配置。

微信小程序安全键盘对用户密码口令加密后的通用格式如下:

'V02_' + sm2(header + timestamp + '\0' + pbkdf_hmac_hex(password, salt) + '\0' + nonce + '\0' + 随机数)

其中,pbkdf_hmac_hex()为安全键盘计算hash的算法表达式,可通过safe-password-custom-hash属性进行设置。 header前两个字节,用于标识密码hash算法:

  1. 0x00 0x00: custom hash
  2. 0x00 0x07: pbkdf_hmac_hex

这个格式考虑了几点安全因素:

  1. 防重放:传入正确的时间戳timestamp,每次加密nonce 保持自增,保证了即使口令相同,每次加密密文也不相同
  2. 防暴力破解:sm2非对称算法本身保证了防暴力破解的可能
  3. 防追溯原文:内置pbkdf_hmac_hex 算法,也可以自定义hash算法;
  4. 防彩虹表攻击:微信小程序开发者可以自定义动态salt;

微信小程序开发者如果打算使用安全键盘,首先在本地生成sm2 秘钥对,然后前往微信小程序管理后台申请微信小程序安全键盘数字证书。证书下发下来后,需要和微信小程序代码一起发布。 微信小程序调用安全键盘时,需要传入微信小程序安全键盘数字证书,完成证书合法性校验之后,再提取证书公钥并采用sm2 算法对用户数据进行加密。 由于采用证书公钥加密,只有用开发者自己持有的私钥才可以解密出数据明文。网络传输过程中,即使密文被恶意拦截,攻击者也无法拿到明文。

如何解密或验密

'V02_' + sm2(header + timestamp + '\0' + hash(password, salt) + '\0' + nonce + '\0' + 随机数)

后台接收到密文后,参照以上格式进行解析: 1、 移除密文4字节前缀; 2、 使用微信小程序安全键盘证书对应的sm2 私钥解密,得到明文数据; 3、 解析明文数据,可以得到时间戳、密码hash、nonce 等字段;

首先,微信小程序开发者后台只能得到脱敏后的密码hash,无法得到明文。当然,根据合规要求,后台也不应该获取到用户密码明文。 其次,微信小程序开发者后台应妥善保存密码hash,作为匹配用户密码是否一致的依据。比如在用户注册或修改密码流程中,后台SM2 私钥解密出密码hash 之后,应当在数据库中(或者其它存储技术)持久化保存该密码hash。在后续用户登录或其它需要校验密码的场景,通过对比用户请求过来的密码hash 和之前保存的密码hash 是否一致,来确定密码是否验证通过。