支付流程安全设计
1、总体流程图
2、流程说明:
由以上流程图可知,整个流程分为:订单生成、支付和后支付验证3个阶段。
2.1、订单生成:
威胁分析:
该阶段最容易犯的错误是根据前端提交的参数来计算应付款额,比如:相信前端提交过来的产品单价,甚至直接在前端计算应付款额再提交给后台进行处理,这种方式的攻击成本非常低,只需要简单篡改数据即可。正确的做法应当是对物品进行编号,根据前端提交的物品编号和数量来计算应付款额,具体步骤如下:
2.1.1、步骤1:
为不同的物品生成物品编号,用户在提交订单时,携带用户购买的物品编号和数量信息给后台生成订单,如下图所示:
2.1.2、步骤2:
电商后台根据用户提交的订单请求参数生成订单数据,其中最重要的一个环节是:根据物品编号和数量计算用户应该支付的款额。即 应付款额=物品编号×购买数量。
2.2、支付:
威胁分析:
支付阶段基本上是按照第三方支付平台的要求进行接入,通过第三方支付平台的签名和签名验证机制保证支付的安全性,但需要注意的是这些支付平台一般都会提供多种不同的签名机制,比如:RSA和MD5,通过申请签名的步骤可以从中分析出一些风险点。
申请RSA签名步骤:
(1)、申请支付接入。
(2)、生成用于签名的PKI证书,将公钥证书上传到第三方支付平台,并将私钥证书和第三方支付平台的公钥证书保存在自己的服务器。
(3)、使用支付SDK接入并产生支付签名。
申请MD5签名步骤:
(1)、申请支付接入。
(2)、在第三方支付平台上设置签名和验证签名所使用的对称密钥。
(3)、使用支付SDK接入并产生支付签名。
通过以上对比,两者之间最大的差异在于第2步,即签名所使用的密钥。MD5签名使用的是对称密钥,即双方使用相同的密钥,这意味着密钥的存储安全性由接入方和第三方支付平台共同保证,双方任意一方泄漏了密钥(比如外部攻击和内部泄漏),都将导致支付安全性无法被保证。反之,RSA签名使用的是非对称的密钥,签名用的私钥只保存在接入方的服务器上,第三方支付平台只保存了对应的公钥,即便第三方平台泄漏了接入方的公钥,也不会影响接入方支付的安全性。除此之外,MD5签名的密钥是通常是由用户手动设置而不是随机生成的,用户可能会设置一个足够长度但不够复杂度的密钥,比如:abcd…xyz012..789,因此很可能会被破解。但两者在接入的难度上并无差别,因此,在可能的情况下,建议使用RSA的签名方式,如果使用MD5的签名方式,应使用足够随机复杂的对称密钥。
2.2.1、步骤3(调用支付平台SDK可实现):
对提交给第三方支付平台的数据进行签名,里面的关键数据包括支付金额等,MD5类型的签名过程如下:
(1)、计算提交参数的签名:
sig= MD5(“amount=389¶m1=$val1¶m2=$val2&…¶mn=$valn”+MD5key);
(2)、组合提交给第三方支付平台的参数(包括签名和签名类型):
amount=389¶m1=val1¶m2=val2&…¶mn=valn& sign=$sig&sign_tpye=MD5
RSA类型的签名过程如下:
(1)、计算提交参数的摘要:
hash = MD5(“amount=389¶m1=$val1¶m2=$val2&…¶mn=$valn”);
(2)、计算提交参数的签名:
sig = RSAsign(hash,RSAprivKey); //此处使用接入方自己的私钥签名
(3)、组合提交给第三方支付平台的参数(包括签名和签名类型):
amount=389¶m1=val1¶m2=val2&…¶mn=valn& sign=$sig&sign_tpye=RSA
2.2.2、步骤4:
将步骤3组合的参数通过用户浏览器重定向到第三方支付平台,以此告知用户实际应付的款额。由于需要经过用户客户端跳转,因此,用户是能够对提交的参数数据进行篡改的(比如篡改金额),由于提交参数经过了签名,篡改参数将导致支付平台验签失败而无法支付成功,但基于以上支付的威胁分析,如果使用的MD5签名密钥因复杂度低可被暴力破解或者密钥通过第三方平台泄漏,那么攻击者就可以在篡改数据后重新计算签名值,成功实施攻击。
2.2.3、步骤5:
由第三方平台实现的签名验证,略。
2.2.4、步骤7(调用支付平台SDK可实现):
用户支付成功后,将调用接入方在第三方支付平台上设置的回调接口,告知接入方用户支付成功,为保证该接口调用过程中数据没有被篡改以及请求没有被伪造,需要进行数据签名验证,MD5类型的签名验证如下:
(1)、获取来自第三方支付平台的请求参数:
param1=val1¶m2=val2&…¶mn=valn& sign=$sig&sign_tpye=MD5
(2)、计算请求参数的签名:
sigLocal= MD5(“param1=$val1¶m2=$val2&…¶mn=$valn”+MD5key);
(3)、对比两者签名sign和sigLocal是否一致
RSA类型签名验证如下:
a) 获取来自第三方支付平台的请求参数:
param1=val1¶m2=val2&…¶mn=valn& sign=$sig&sign_tpye=RSA
b) 计算请求参数摘要:
hash = MD5(param1=val1¶m2=val2&…¶mn=valn)
c) 计算请求参数签名:
sigLocal = RSAversign($sig,RSA3rdPubKey); //注意此处使用第三方支付平台公钥验签
d) 对比两者签名sign和sigLocal是否一致。
2.3、后支付验证(待确认):
威胁分析:
通过步骤4的分析可知,如果接入方的密钥已经泄漏,那么攻击者就可以任意篡改订单数据和生成签名,因此需要进行一次反向验证(或者由支付平台回调告知),确保支付平台已支付的金额和接入方订单数据内的预期金额是一致的,如果不一致表示该订单存在异常,应予以作废处理,这样也符合产品纵深防御的安全设计。
2.3.1、步骤8:
(1)、接收并处理来自第三方支付平台的支付成功回调。
(2)、根据流水号去第三方支付平台查询实际支付金额(或由支付平台回调告知)。
(3)、对比接入方的预期支付金额与实际支付金额是否一致
注:此过程同样需要签名和验签。