有了前文几个步骤的分析和设计,TYPESDK的信息交互流程已经可以正常工作了,但是,这个流程还没有考虑到支付这样的过程中,至关重要的信息安全问题。
在整个交互过程中,游戏服务端,SDK服务端,渠道服务端都属于安全区域,这部分发生的数据交互,基本是可以信任的,只需要作相对简单的处理工作;而客户端,包括游戏客户端,SDK客户端都属于危险区域,在这部分产生的数据和请求,都有可能受到外部的拦截和篡改。因此,我们需要在流程上加以预防和控制。
图1
从示意图1可以看出。针对三类不同安全性的数据流,我们的处理原则也是不同的。
l 蓝色箭头所表示的是安全可信的请求。属于这类请求的是游戏服务端与SDK服务端之间通信的请求。对这类请求传送的信息,原则上可以直接予以信任,并且以之作为基础,完成流程和逻辑的控制。在TYPESDK的设计中,对这部分请求,设置的安全机制是简单的通信key签名校验。
l 绿色箭头所表示的是半可信的请求。具体来说包括渠道服务端与SDK服务端之间的通信请求。由于渠道服务端处于开发者可知范围之外,虽然绝大多数情况下该地址是可靠的,但是在将交互信息用于逻辑之前,需要做安全校验。通常采取的措施有:
n 数据摘要签名/请求串加密。这部分处理通常在渠道服务端文档内会写明处理逻辑及校验算法。
n 访问IP白名单控制。在SDK服务器上配置指定渠道IP白名单,只信任来自白名单内IP的请求。同样,部分渠道也会通过后台配置或技术人员联系的形式设置开发者的服务器IP为白名单。
l 红色箭头表示的是不可信的请求。基本上,与客户端通信的所有请求都是不可信的请求。由于各请求隶属的模块不同,处理方式也各有分别。基本上有以下几种:
n 渠道客户端库与渠道服务端之间的通信,这部分数据交互由渠道本身机制来保证其可靠性,采用的技术手段包括token验证,客户端加密,签名身份识别等等机制。对于开发者而言,可以简单的认为,从渠道提供的接口获得的数据是可信的,提交给渠道的信息也不用作多余的安全处理机制,否则可能会产生其他问题。
n 游戏客户端与服务端之间的通信,相信已经有很多专述文章说明。常见的安全检测机制包括token,时间戳,签名等校验方式。但是专注于安全的渠道库并非专注游戏性的游戏客户端可比。出现破解或者请求被拦截的情况可谓司空见惯。因此,在游戏逻辑架构设计的阶段就要将安全性要求考虑在内。遵循以下原则:
1. 原则上重要逻辑必须使用服务端计算,客户端只负责展示和效果。例如前一篇文字里提到过的创建订单逻辑,由服务端产生订单。客户端可以接收订单号然后展示给用户,以及传送给渠道客户端。
2. 原则上所有数据信息都以服务端保留的记录为准,客户端提供的记录或者数据只作为参考比对。有冲突时服从服务端。例如,用户破解了客户端并且篡改了客户端传输给渠道客户端的内部订单号。在后续的发货及对账时,游戏服务端只信任服务端自己保持的订单状态,对不符合的订单直接抛弃或留记录不处理。
n SDK客户端与服务端之间的通信,这部分数据交互并不多,通常适用于一些token转发,换签之类的特殊渠道逻辑,后文会有详细说明。SDK服务端对这些不安全数据的处理方式是保留记录以资比对,但不作为任何逻辑的根基和依据。
根据以上原则,我们分析了各类数据交互的安全性特点以及处理原则,很容易就会发现,在我们原本的支付-发货逻辑中,缺失了重要的一环,即订单校验步骤。没有这一步骤,SDK服务端在收到渠道的发货回调后,会立刻通知游戏服务端处理发货逻辑。然而根据以上的分析,渠道支付订单的提交步骤事实上是在不安全区域-客户端内完成的,这一步骤可以被别有用心的用户拦截并修改。举例如下:
例1:用户点击游戏中的大礼包购买,生成了一笔金额为648元的订单,然后使用非法工具在渠道客户端提交订单时,拦截该订单,并修改其金额为0.01元,随即完成支付。渠道异步回调给SDK服务端时,SDK服务端判定其为合法订单并通知游戏服务端发货。结果是用户成功获取了非法利益648元。
例2:用户通过非法工具,获取并保存了自己一笔正常订单648元的所有订单信息。随后通过技术手段,将该订单的回调信息重发给了SDK服务端。该订单一切信息都与前一笔正常支付相同,SDK服务端判定其为合法订单并通知游戏服务端发货。结果是用户又成功获取了非法利益648元。
如何避免例子中的情形发生?比较简单的手段是在游戏服务端进行订单比对和信息校验,但是这样的处理不可避免的要和具体渠道的订单字段和处理逻辑挂钩,例如,渠道A的回调信息中含有订单金额字段,可以用于比对,而渠道B的回调信息中只有单价和数量字段,如需比对只能自己计算总金额。又例如,渠道C会定期做优惠活动,充值折扣打9折,返回的充值金额也是9折后的数值,和原本游戏订单中的金额不一致。这些逻辑不可能都让游戏服务端来处理,否则,就失去了聚合SDK的意义。因此,我们需要让游戏服务端提供一个订单查询verify接口,每当SDK服务端接收到渠道的回调请求时,调用该接口,进行订单比对,只有比对通过时,才会通知游戏服务端发货,这样就比较圆满的解决了这种情形。
但是,SDK服务端并不能解决所有问题,游戏服务端还是需要把好最后一道关。例如合法订单的重复发送逻辑,就需要游戏服务端处理好,接收到重复的订单号时,不要重复发货。
修改后的发货逻辑如下图所示:
图2
流程说明
1. 充值订单到帐后,渠道服务端异步通知TYPESDK服务端
2. TYPESDK服务端通过订单中的内部订单号,查询充值信息提交接口提交的订单信息,获取游戏订单查询URL,向该URL发起请求获取游戏服务端保存的订单信息,用于对比判定该订单是否合法。
3. 游戏服务端通过提交的内部订单号查询订单信息,返回给TYPESDK服务端
4. TYPE服务端校验通过,通知游戏服务端发货
5. 游戏服务端收到发货请求后先保存该请求,立刻返回TYPESDK服务端,表示已收到发货请求。将该订单的状态置为已付款未发货。
l 不要处理完发货请求之后再返回,可能造成渠道通知请求超时,导致该通知重复发送
l 注意渠道为保证通知到达,可能会重复发送发货请求,TYPESDK会原样转发所有请求。游戏服务端需要做防止同一订单号重复发货的处理。
6. TYPESDK返回渠道服务端
7. 游戏服务端异步处理发货逻辑,将该订单的状态置为已发货。并通知游戏客户端