勤勤学长 发表于 2020-1-28 21:23

php开发第四方支付系统过程记录(1):起因及为什么参数会有sign签名

本帖最后由 勤勤学长 于 2020-1-28 23:52 编辑

起因:
今晚想做个支付页面,参考了一下腾讯云的充值页面。因为现在的支付功能有网站在用着,直接改动也可以用于新网站的话,难度很大。突然想起之前做过集合型的支付系统。找到了17年做的商城系统,那时候用的是这套支付,由于想过把代码开源,把里边的商品信息给清空了,进不了支付页面参考代码,怎么都进不去后台添加。

直接看支付系统的代码,一时间也看不懂了,换过一次服务器,清理过一些数据库,数据也丢失了。可能要重写支付系统。

把支付功能独立出来也是有好处的吧?规范代码之后,即便以后再有新的网站需要支付功能,直接提供本地订单号、付款金额、回调URL就可以生成对应数据的支付订单支付页面,支付产生的数据单独数据库储存,查询也是直接开接口出来。后期也可以把接口开放给一些客户使用。

为什么要计算sign?
可能有些开发新手会好奇,尤其一些网站发起支付的时候,会有一个参数sign。如果是用作区别是谁发起的,直接用key不就行了?为什么还要算出sign?
一般情况下,生成一个支付,需要提交本地订单号、支付金额、商品名称。这样就谁都可以发起支付生成订单,数据库会产生一堆无用数据。

/pay?title=勤勤学长支付商品名称&money=10.08&out_trade_no=202001281234

如果是加key的话,可以区分是提交商户,再谁都可以发起支付生成大量无用订单数据的前提下,每次生成订单之前还查询一次key存在不存在。

/pay?title=勤勤学长支付订单名称&money=10.08&out_trade_no=202001281234&key=byGY8B4

如果是用sign,就是一种简单的技术基础过滤,你提交的之前到支付接口的时候,接口直接就可以判断你提交的数据跟sign是否一致了。

一般情况下,sign值是通过提交参数的拼接加密而成,比如本地订单号、支付金额、商品名称,生成了一个sign。
$sign=md5('title=勤勤学长订单名称&money=10.08&out_trade_no=202001281234&key=byGY8B4');
//计算得438574b9d68b642f2493c4cd16b5a1cd

/pay?title=勤勤学长支付订单名称&money=10.08&out_trade_no=202001281234&key=byGY8B4&sign=438574b9d68b642f2493c4cd16b5a1cd

服务端接收的是两部分信息:接收参数+sign值。
参数:title=勤勤学长支付订单名称&money=10.08&out_trade_no=202001281234&key=byGY8B4
sign:438574b9d68b642f2493c4cd16b5a1cd

如果把其中一个值改变(如支付金额、订单号),通过服务端计算出来的sign就与之前的不一样了。
比如说我把金额改为11.08,其他值均不改
/pay?title=勤勤学长支付订单名称&money=11.08&out_trade_no=202001281234&key=byGY8B4&sign=438574b9d68b642f2493c4cd16b5a1cd

服务端接收
参数:title=勤勤学长支付订单名称&money=11.08&out_trade_no=202001281234&key=byGY8B4
sign:438574b9d68b642f2493c4cd16b5a1cd
服务端计算参数
$sign=md5('title=勤勤学长支付订单名称&money=11.08&out_trade_no=202001281234&key=byGY8B4');
//计算得48c8573c1ebfc56a06f3e08072400be5
与接收到的sign不一致,服务端就可以直接返回错误提示,不会再进行数据库操作。
reture ‘sign错误’;

如果接收到的sign和接收到参数计算出来的sign一致,再通过key查询发起支付的商户是谁。信息不存在返回 reture ‘key错误或不存在’;

商户存在再真正的发起支付。

我也是小白,边做边学,哪里不对或者做得不好的地方还请指出。

2020年1月28日23点45分更新
关于有前辈提到key存在泄露风险,前文提到生成订单的方式是通过get方式,只是方便大家理解。

目前我想到的方式有两种:
1、把get提交改成post提交,服务端校对通过后,直接跳转到带着订单号的支付页面。
比如:/pay?out_trade_no=20200128123456
但是其他用户可以修改订单号访问链接会触发数据库查询操作。

2、弄一个对称加解密方式,提供开发者加密方式
例如把字符串
title=勤勤学长支付订单名称&money=11.08&out_trade_no=202001281234&key=byGY8B4&sign=438574b9d68b642f2493c4cd16b5a1cd
加密成类似/pay?order=byGY8B4XbXL7X4GM8K99Xq9gLM8KFY88

我在服务端通过接收order值解密之后再校对,解密失败或sign错误就能直接拦截了。

smldhz 发表于 2020-1-28 21:37

对sign的理解和key的使用有问题,sign用处是对订单信息签名,防止伪造。key是用来参与sign计算的。一旦key泄露,用户就能自己伪造任意的订单信息。

VE5514 发表于 2020-1-28 22:29

key不能泄露的

勤勤学长 发表于 2020-1-28 23:43

smldhz 发表于 2020-1-28 21:37
对sign的理解和key的使用有问题,sign用处是对订单信息签名,防止伪造。key是用来参与sign计算的。一旦key ...

key通过get的方式提交确实存在泄露风险。
那如果我用post方式提交到服务端,服务端校对无误之后再返回并跳转订单支付页面呢?
比如/pay?out_trade_no=20200128123456
或者找一种对称加密方法,提供开发者加密方式。把字符串
title=勤勤学长支付订单名称&money=11.08&out_trade_no=202001281234&key=byGY8B4&sign=438574b9d68b642f2493c4cd16b5a1cd
加密成类似/pay?order=byGY8B4XbXL7X4GM8K99Xq9gLM8KFY88
我在服务端解密之后再校对。:$qqq

smldhz 发表于 2020-1-29 00:14

勤勤学长 发表于 2020-1-28 23:43
key通过get的方式提交确实存在泄露风险。
那如果我用post方式提交到服务端,服务端校对无误之后再返回并 ...

get转post没有任何意义、只是请求方式不同,该传输什么还是得传输,简单说就是掩耳盗铃。
只要加密过程是在客户端进行,加密也没有意义,先不说能否解密,你加密前的东西不也在客户端?同样掩耳盗铃。

thinkingbullet1 发表于 2020-1-29 09:50

所以现在都用非对称加密了

zx2000 发表于 2020-1-30 21:18

刚刚入门,问一下 前端的算出的sign加密方法是不是浏览器直接可以看得到?打比方md5加密 那要是先伪造一波数据然后在自己md5加密一遍再发送呢?

勤勤学长 发表于 2020-1-30 22:12

zx2000 发表于 2020-1-30 21:18
刚刚入门,问一下 前端的算出的sign加密方法是不是浏览器直接可以看得到?打比方md5加密 那要是先伪造一波 ...
正如楼上的前辈所说,sign是签名防止订单伪造。浏览器能看到的是get提交方式。同时也需要让服务端知道你给的sign是什么内容。要做可逆加密,对称或非对称。md5就是不可逆了。
可以看看我新的帖子 https://www.52pojie.cn/thread-1096281-1-1.html

你给的安全感 发表于 2020-1-31 08:18

本帖最后由 你给的安全感 于 2020-1-31 08:20 编辑

Key只参于加密,不参于数据传输!怎么会涉漏
(貌似看你直接把key作为参数传递了,不涉漏才怪呢)
页: [1]
查看完整版本: php开发第四方支付系统过程记录(1):起因及为什么参数会有sign签名