跳到主要内容

服务端回调验签机制

前言

为了保证数据安全,我们提供了签名机制。虽然防火墙白名单起到了一定的作用,并且不对内容验签功能也能完成,但是我们还强烈建议游戏对回调内容验签以避免安全漏洞。

验签过程

从请求中获取验签字段

字段说明示例
HTTP 请求方法(Method)获取 HTTP 请求的方法(GET、POST、PUT)等POST
Path获取请求的绝对 URL,并去除域名部分得到参与签名的 URL。如果请求中有查询参数,请去除请求参数只保留请求路径/test/v1/callback/receive
应答时间戳(Timestamp)HTTP 头 Timestamp 中的应答时间戳1642646059
应答随机(Nonce)HTTP 头 Nonce 中的应答随机字符串7b872f48-5a86-4665-8d1c-da3827698ec9
应答报文主体(body)response Body 需要按照接口返回的顺序进行验签(字段顺序不能变),GET 请求为空{"trxNo":313624737144475650,"status":2}
备注

response Body 需要按照接口返回的顺序进行验签,字段顺序不能变。

构造验签串

按照以下规则构造应答的验签名串。签名串共有三行,行尾以 \n 结束,包括最后一行。\n 为换行符(ASCII 编码值为 0x0A)。若应答报文主体为空(如 HTTP 状态码为 204 No Content),最后一行仅为一个 \n 换行符。

HTTP 请求方法 \n
URL\n
应答时间戳 \n
应答随机串 \n
应答报文主体 \n

假设游戏服务端提供的支付结果回调地址为 : https://gameserver.xd.com/test/v1/callback/receive,游戏服务端在接受到回调时的 HTTP 报文为:

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 02 Apr 2019 12:59:40 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 2204
Connection: keep-alive
Keep-Alive: timeout=8
Content-Language: zh-CN
Request-ID: e2762b10-b6b9-5108-a42c-16fe2422fc8a
Nonce: 7b872f48-5a86-4665-8d1c-da3827698ec9
Signature: UmwMNlOA3+/MsMm5GaTog5PR8R657VVtozKw6WMSPIEkzzYUIzj0Kz+UR3VMg2ga2kQcnFIcvnlr96jTeHpTy8ixD2UINdmQOovfMAmk8SYG9Lyyed+IHkxL/zJ8JDgK6+5VrlhicAIAEyOcqbWItFNQDeH/fq0erVD+pvVPJJi4SQRNLzFGZvueTee7l2q684eupM4vUK/6Zivp5QhCW9dBX28FlQ3v5WWv8eKrzzudmxxBnQiKY4hu0pWTUAcsixlJWNKdOfRKpU6COMVW8cRa1X9cTQsL9gm1peOpVB0XTRtGilc6pWuCPSJOiDjYpIaSFOE9pEFILGjZts/SaQ==
Timestamp: 1642646059
Cache-Control: no-cache, must-revalidate

{"bankTrxNo":"","trxNo":313624737144475648,"notifyTime":1642646059424,"channel":1,"trxType":0,"userId":"313624253256011776","outTrxNo":"897298475","platform":1,"paymentType":0,"products":[{"productCode":"global.recharge.coin2.99","quantity":1}],"totalAmount":30.000,"appId":1111,"currency":"USD","notifyId":313625136534491136,"attach":{"gameServerId":"999","gameExt":"","gameRoleId":"5559981"},"status":2}

则验签字符串(称为:signBase)为:

POST
/test/v1/callback/receive
1642646059
7b872f48-5a86-4665-8d1c-da3827698ec9
{"bankTrxNo":"","trxNo":313624737144475648,"notifyTime":1642646059424,"channel":1,"trxType":0,"userId":"313624253256011776","outTrxNo":"897298475","platform":1,"paymentType":0,"products":[{"productCode":"global.recharge.coin2.99","quantity":1}],"totalAmount":30.000,"appId":1111,"currency":"USD","notifyId":313625136534491136,"attach":{"gameServerId":"999","gameExt":"","gameRoleId":"5559981"},"status":2}

获取签名 Signature

游戏服务端通过 HTTP 头里 Signature 字段获取到平台经过加密的签名(称为 :Signature)。对 Signature 的字段值使用 Base64 进行解码,得到签名(称为 :sign)。

使用公钥验签

使用平台公钥对验签名串(signBase)和签名(sign)进行 SHA256 with RSA 签名验证。

信息

平台公钥在开通支付回调或者账户回调时平台会提供给游戏

命令行验签示例

下面展示使用命令行简单演示如何进行验签 :

  • 首先,获取平台公钥
cat cert.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4mpfpysBNNe43XXANDpT
UD4itQ/9HjhaqW2uwufwKm9YFoYOQ7c80N5J6J7gpswYHiFsn0uE/f3ybNwhGczJ
ayPM/i8Jcbiak/28Q62s+xg26Ju2WI1/CD/xdxSJEpnPiSPUv5az1SUIlu0/2b7U
1N0j+VqaS+T4odnkvrkoVnK25ejQkNapzlQXuBHlXjnn0RmevfoKwKazxUkua1A8
gPsRFM1PrARgpIB5LmiimjLQqXmYulhB236ZSQMB6Yj3VBtt/6zOwYNe1fr7ug7S
3GkZGywDzzaz8bEQvr6VhleXGZAvN4FJIRJN1ypcyXgLR8ofMMYVwCiBKoLJ0IZI
7wIDAQAB
-----END PUBLIC KEY-----
  • 然后把签名 base64 解码后保存为文件 signature.txt
openssl base64 -d -A <<< 'UmwMNlOA3+/MsMm5GaTog5PR8R657VVtozKw6WMSPIEkzzYUIzj0Kz+UR3VMg2ga2kQcnFIcvnlr96jTeHpTy8ixD2UINdmQOovfMAmk8SYG9Lyyed+IHkxL/zJ8JDgK6+5VrlhicAIAEyOcqbWItFNQDeH/fq0erVD+pvVPJJi4SQRNLzFGZvueTee7l2q684eupM4vUK/6Zivp5QhCW9dBX28FlQ3v5WWv8eKrzzudmxxBnQiKY4hu0pWTUAcsixlJWNKdOfRKpU6COMVW8cRa1X9cTQsL9gm1peOpVB0XTRtGilc6pWuCPSJOiDjYpIaSFOE9pEFILGjZts/SaQ==' > signature.txt
  • 最后,验证签名
openssl dgst -sha256 -verify cert.pem -signature signature.txt << EOF
POST
/test/v1/callback/receive
1642646059
7b872f48-5a86-4665-8d1c-da3827698ec9
{"bankTrxNo":"","trxNo":313624737144475648,"notifyTime":1642646059424,"channel":1,"trxType":0,"userId":"313624253256011776","outTrxNo":"897298475","platform":1,"paymentType":0,"products":[{"productCode":"global.recharge.coin2.99","quantity":1}],"totalAmount":30.000,"appId":1111,"currency":"USD","notifyId":313625136534491136,"attach":{"gameServerId":"999","gameExt":"","gameRoleId":"5559981"},"status":2}
EOF
Verified OK

如果是 Get 请求,请注意 Get 请求的 body 为空,并且不带请求参数,例如:

  • 首先,获取平台公钥
cat cert.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkC4PyYuf1JBTL29zCoQ7amElS0hNcW1J
qttK8b4fCjQxLKdzKBDNKJW09Iadd1CiWBj2DEd1bQbWz0ukF+8jSbwLL5BUuy/Wpacu3aRhEKd0
aUzUSmkHtmEfBSuM3AQ7oCs0QBhlk9AdBXIEC3NY87ch2rPCjInVHzRj6kyATya7nfkamZCpiFzM
wxZEnkHn7r3Jh87s8X5UviudYvdHpNvnTQ5MmvBv09FogjgnPTdP6MzW1fcclIeTAzhIaYhhXKMZ
9ylqr29Q8XBPgNZqFOhNkFPADXzSiJNUbclLTAJQTmzF4hsLfOE553LKVSapU0M8jE2tHLAALW2o
hNh9awIDAQAB
-----END PUBLIC KEY-----
  • 然后把签名 base64 解码后保存为文件 signature.txt
openssl base64 -d -A <<< 'W0UQ9arPHlPvPwYYg+XI8+RcvotsW933nJVwoEMwF1OuQXpBv9Zu+Eg9H0RJQBuV7BKXwJIGTmkeVCM3KYKpFB7e4Tl+zQ2n7mUFjF2TpIj32LXK7BzbQhFFro8NZrb5IQN5Xs5SGyDqUnrlXCLPLNM9pOo617cwDOBihC8rqNWSeVDaUWzsgJzn69o64kqQYxuHP4tqbuVZSZdTKe+q5oRiqqK9f+NphYWK7FuQhSIQBFPyZc//+jP9mxypTP+TdKl7pEVlUxytekhLtDJGewZG7o8Lnvea3IZAUUqIZrlisz8Oi6l+84nNBJIwEMDSHXiMQDGif0NgZfnhGu/iNA==' > signature.txt
  • 最后,验证签名
openssl dgst -sha256 -verify cert.pem -signature signature.txt << EOF
GET
/test/v1/game/role
1663747778
2439c7f9-c355-4c65-9d87-eb1de9bd8616

EOF
Verified OK

FAQ

Q: 支付回调和账户回调的验签机制是否一致?

A: 是一致的。为了避免多次开发,支付结果的回调和账户注销的回调通知验签机制保持一致。如果游戏已经接入过账户注销的回调通知,那账户和支付的验签公钥是一样的。如果没有则联系平台研发提供。

Q: 为什么验签始终对不上?

A:建议从如下几步骤排查:

  • 检查从 HTTP 请求中获取的验签字段是否正确,比如 body 字段的字符串顺序不能变 。
  • 排查代码是否有问题(该算法已经在多个游戏上验证过,理论上不存在问题)。
  • 联系平台研发一起排查。