• iOS内购 服务端票据验证及漏单引发的思考.


    因业务需要实现了APP内购处理,但在过程中出现了部分不可控的因素,导致部分用户反映有充值不成并漏单的情况。

    仔细考虑了几个付费安全上的问题,凡是涉及到付费的问题都很敏感,任何一方出现损失都是不能接受的,所以在这里整理一些支付安全的要点分享一下。

    支付方式

    IAP是指In-App Purchase, 是一种付费方式,而并不是苹果专有的付费方式,在其它平台上也会有不同的实现,这里针对Apple IAP。

    说到IAP安全问题,在苹果的IAP流程中有一个比较明显的逻辑漏洞,这个逻辑漏洞是建立在我们处理不当的情况下发生的,会导致己方提供的服务和用户之间出现问题。先看看IAP支付时序图:

    支付流程

    1.客户端向Appstore请求购买产品(假设产品信息已经取得),Appstore验证产品成功后,从用户的Apple账户余额中扣费。

    2.Appstore向客户端返回一段receipt-data,里面记录了本次交易的证书和签名信息。

    3.客户端向我们可以信任的服务器提供receipt-data

    4.服务器对receipt-data进行一次base64编码

    5.把编码后的receipt-data发往itunes.appstore进行验证

    6.itunes.appstore返回验证结果给服务器

    7.服务器对商品购买状态以及商品类型,向客户端发放相应的道具与推送数据更新通知

    8.客户端收到服务器的处理状态,进行相应的结单处理

    这八个步骤实际上是一个很安全的流程了。那问题出在哪里呢?我们谈谈两种苹果IAP的验证模型。

    验证方式

    1.IAP built-in Model,本地验证

    有些APP甚至是网游,都直接跳过了3~7步骤,在第2步拿到receipt-data之后,直接由客户端向itunes.appstore发送验证请求,并且拿到结果,根据结果修改数据。

    我们在设计APP的时候都遵循一个真理,“凡是在客户端的数据都是不安全的”,深以为然。如果没有独立服务器辅助验证,这样也就避免不了数据被修改的事实了,是的,你会少赚钱。

    不过如果APP也不通过独立服务器验证,而是在客户端验证之后再告知服务器状态让其发放游戏道具,那就太可怕了点。这是IAP built-in Model

    那是不是就完全不能让这个过程变得安全了呢?也不是,但这个安全保障只是让修改变得困难而已。苹果官方提供了 Validating Receipts Locally 在客户端对receipt-data进行安全验证,主要是对证书以及签名的合法性验证。如果不想自己写代码验证,也可以借助第三方机构提供的receipt-data验证API,比较著名的有  urbanairship和  beeblex 。

    但如果能伪造一个完全合法的receipt-data,是不是一样可以达到欺骗目的。是的,为了绕过Validating Locally,于是黑客开始用自己伪造的receipt-data进行移花接木,所以出现了可以伪造”合法订单”的 in-appstore 。因此这种本地加强验证的方法也不能完全避免IAP攻击。

    2.IAP Server Model,服务器验证

    而如果我们把验证逻辑移到服务器上,这个过程就变得容易多了。因为不再需要担心receipt-data被伪造的问题。不过就算把步骤4~7在服务器上做了,同样也会产生一些幼稚的逻辑漏洞

     对验证receipt-data的reponse content不进行验证和记录,只根据Product直接发放商品。这样只要客户端不断提交receipt-data,按照正常逻辑你就需要不断验证并且重复发放商品。较为安全的做法是:

    在每一次收到receipt-data之后,都把提交的用户账号以及receipt-data中的单号建立映射并记录下来,在每次验证receipt-data时,先判断其是否已经存在。

    只要做了这样的验证,整个支付流程都变得明朗起来。

    确保receipt-data的成功提交与异常处理

    建立在IAP Server Model的基础上,并且我们知道手机网络是不稳定的,在付款成功后不能确保把receipt-data一定提交到服务器。如果出现了这样的情况,那就意味着用户被appstore扣费了,却没收到服务器发放的道具。(这样就引发了漏单)

    解决这个问题的方法是在客户端提交receipt-data给我们的服务器,让我们的服务器向苹果服务器发送验证请求,验证这个receipt-data账单的有效性. 在没有收到回复之前,客户端必须要把receipt-data保存好,并且定期或在合理的UI界面触发向服务端发起请求,直至收到服务端的回复后删除客户端的receipt账单记录。

    如果是客户端没成功提交receipt-data,那怎么办?就是用户被扣费了,也收到appstore的消费收据了,却依然没收到道具,于是投诉到客服处。

    这种情况在以往的经验中也会出现,常见的用户和运营商发生的纠纷。客服向用户索要账号和appstore的收据单号,通过查询itunes-connect看是否确有这笔订单。

    如果订单存在,则要联系研发方去查询服务器,看订单号与用户名是否对应,并且是否已经被使用了,做这一点检查的目的是 为了防止恶意用户利用已经使用过了的订单号进行欺骗(已验证的账单是可以再次请求验证的,曾经为了测试,将账单手动发给服务器处理并成功),谎称自己没收到商品。

    当然了,如果查不到这个订单号,就意味着这个订单确实还没使用过,手动给用户补发商品即可。

    有朋友问怎么通过itunes-connect查看具体订单,itunes-connect中无法直接看到订单信息,可以用以下方法来查询

    1.可以通过账单向苹果发送账单验证,有效可以手动补发

    2.用自己的服务器的记录账单列表对比 

    3.利用第三方的TalkingData等交易函数,会自动记录账单数据

    建议

    为保证审核的通过,需要在客户端或server进行双重验证,即,先以线上交易验证地址进行验证,如果苹果正式验证服务器的返回验证码code为21007,则再一次连接沙盒测试服务器进行验证即可。

    在应用提审时,苹果IAP提审验证时是在沙盒环境的进行的,即:苹果在审核App时,只会在sandbox环境购买,其产生的购买凭证,也只能连接苹果的测试验证服务器,如果没有做双验证,需要特别注意此问题,否则会被拒。

    其他

    在sandbox中验证receipt:https://sandbox.itunes.apple.com/verifyReceipt

    在生产环境中验证receipt:https://buy.itunes.apple.com/verifyReceipt

    那么如何自动的识别收据是否是sandbox receipt呢?
    识别沙盒环境下收据的方法有两种:

    1. 根据收据字段 environment = sandbox。
    2. 根据收据验证接口返回的状态码。
      如果status=21007,则表示当前的收据为沙盒环境下收据, 进行验证。

    苹果反馈的状态码

    • 21000 App Store无法读取你提供的JSON数据
    • 21002 收据数据不符合格式
    • 21003 收据无法被验证
    • 21004 你提供的共享密钥和账户的共享密钥不一致
    • 21005 收据服务器当前不可用
    • 21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
    • 21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
    • 21008 收据信息是产品环境中使用,但却被发送到测试环境中验证

    作者:旧旧的 <393210556@qq.com> 解决问题的方式,就是解决它一次

  • 相关阅读:
    MySQL索引底层数据结构
    numpy和matplotlib读书笔记
    Python turtle学习笔记 #11933
    turtle笔记
    五角星绘制
    六角形绘制
    叠加等边三角形绘制
    什么叫方法签名
    Java编程思想 第七章
    类加载机制
  • 原文地址:https://www.cnblogs.com/widgetbox/p/7529970.html
Copyright © 2020-2023  润新知