wechatpay-apiv3 / wechatpay-php

微信支付 APIv3 的官方 PHP Library,同时也支持 APIv2
Apache License 2.0
474 stars 98 forks source link

不恰当的使用Rsa::decrypt函数(使用OPENSSL_PKCS1_PADDING填充方案),可能引起应用层存在被攻击的风险 #133

Open TheNorthMemory opened 1 month ago

TheNorthMemory commented 1 month ago

运行环境

- OS:MacOS
- PHP: 8.3.7
- wechatpay-php: 1.4.9

描述你的问题现象

这可能不应该是件公开讨论的事宜,有关应用安全的问题可能需要具有专业知识的人来共同探讨,然而此问题在开源社区有了许多公开讨论,暂且先把问题及可能的解决方案记录于此。

缘由

在例行性更新本地PHP版本的时候,按操作习惯,会run一遍测试用例,然而有许久如下测试用例有一条F一直悬而未决:

https://github.com/wechatpay-apiv3/wechatpay-php/blob/2cabc8a15136050c4ee61083cd24959114756a03/tests/Crypto/RsaTest.php#L307-L321

现状

翻阅了PHP的更新历史及OpenSSL的更新历史,发现如下两条重要更新:

PHP自8.1.0起,使用了OpenSSL的EVP_PKEY API,且OpenSSL自3.2.0起,已显式调整EVP_PKEY_decrypt处理逻辑,默认当填充方式为RSA_PKCS1_PADDING解密失败时,不再抛异常,代为输出为随机字符串。这即是 "encrypted as OPENSSL_PKCS1_OAEP_PADDING, and decrpted as OPENSSL_PKCS1_PADDING" 用例测试未通过的原因所在。

公开的CVE/patch/backport如:

都提到了一点是,RSAES-PKCS1-v1_5已不再安全并且容易受到攻击。

受影响

目前已知在微信支付海外版,有如下两个接口声明的非对称加密方案为RSAES-PKCS1-v1_5:

身份信息校验API

• 使用微信支付平台证书的公钥,对于需要加密的参数值进行 RSA 加密。填充方案使用 RSAES-PKCS1-v1_5

Onboard Sub-merchant API

1、Perform the RSA encryption for the parameter values with the public key of the WeChat Pay Platform certificate. Use RSAES-PKCS1-v1_5 as the filling scheme.

解决方案

服务端

建议微信支付团队,评估影响等级及寻求解决方案。

客户端

本SDK(wechatpay-php)自1.2.1起,提供了OPENSSL_PKCS1_PADDING加解密支持,目前客户端暂时未发现使用OPENSSL_PKCS1_PADDING解密的场景,出于兼容性及安全考虑,需要:

  1. 调整 \WeChatPay\Crypto\Rsa::decrpt 函数,当使用OPENSSL_PKCS1_PADDING填充方案时,给予废弃及安全提示,不再推荐使用;
  2. 尝试兼容PHP低版本的ext-openssl(OpenSSL<3.2.0)解密失败抛异常行为,与OpenSSL>3.2.0默认行为保持一致,代为输出为随机字符串;
  3. 基于以上两点,修正 WeChatPay\Tests\Crypto\RsaTest::testCrossEncryptDecryptWithDifferentPadding用例覆盖;
xy-peng commented 1 month ago
  1. 同意不再支持 OPENSSL_PKCS1_PADDING
  2. 业务侧存量的功能不太方便直接去掉
  3. 更进一步,PHP SDK 能否支持国密,已替代不安全的敏感信息加密,供对安全有更高要求的商户使用(实际会比较麻烦)