代码之家  ›  专栏  ›  技术社区  ›  Dan Fabulich

防止重播攻击appStoreReceiptURL app receipts

  •  6
  • Dan Fabulich  · 技术社区  · 7 年前

    我们有一个服务器端服务,我们只想为我们的付费iOS应用程序的有效用户提供。(请注意,这是一个付费iOS应用程序,而不是带有IAP的免费应用程序。)

    当我们使用 appStoreReceiptURL 要检查沙盒应用程序收据并将其发送到我们的服务器端,我们会看到如下收据:

    {
      "receipt_type": "ProductionSandbox",
      "adam_id": 0,
      "app_item_id": 0,
      "bundle_id": "com.example.myapp",
      "application_version": "1.1.1",
      "download_id": 0,
      "version_external_identifier": 0,
      "receipt_creation_date": "2018-04-16 23:53:58 Etc/GMT",
      "receipt_creation_date_ms": "1523922838000",
      "receipt_creation_date_pst": "2018-04-16 16:53:58 America/Los_Angeles",
      "request_date": "2018-04-17 03:25:42 Etc/GMT",
      "request_date_ms": "1523935542798",
      "request_date_pst": "2018-04-16 20:25:42 America/Los_Angeles",
      "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
      "original_purchase_date_ms": "1375340400000",
      "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
      "original_application_version": "1.0",
      "in_app": []
    }
    

    我担心这张收据会遭到重播攻击。在重播攻击中,一个设备购买应用程序并提交有效回执,但另一个未经授权的设备存储并传输第一个回执的准确副本。由于第一张收据由苹果公司签署,因此副本似乎有效。

    理想情况下,我们可以通过观察回执中的唯一标识符来击败重播攻击;如果有人试图重新传输相同的收据ID,我们就会知道它是重复的。IAP收据包括 transaction_identifier 正是因为这个原因。

    但似乎没有唯一的标识符,我们可以用来识别付费应用收据的重播攻击。黑客可以从不同的设备将这张收据重新传输给我们,我们无法知道这是重复的收据还是新的原始收据。

    说到这里,我的眼睛被那些 _id 沙盒收据中为0的数字: adam_id ,则, app_item_id download_id .我们能用其中任何一个来识别重复收据吗?还是有其他更好的处理方法?

    4 回复  |  直到 7 年前
        1
  •  1
  •   Dan Fabulich    7 年前

    无法检测付费应用的重复收据。这个 adam_id ,则, app_item_id download_id 没有文档记录,因此开发人员不能依赖它们来实现安全目的。这与IAP收据不同,IAP收据包括 transaction_identifier 可以消除重复数据。

    不过,有一种可能的解决方法。您可以向用户提供价格为0美元(免费)的非消耗品IAP,并要求用户“购买”它以访问服务器端功能。由于这是一个非消耗品IAP,每个付费应用程序用户最多只能购买一次。

    对于用户来说,同意免费“购买”并登录应用商店访问它有点麻烦,但一旦同意,他们就会拥有一个与付费应用收据相关的IAP。免费IAP收据包括交易标识符;服务器可以使用事务ID来消除购买的重复数据。

        2
  •  0
  •   christopherdrum    7 年前

    据我所知,您要查找的唯一标识符在收据的JSON格式中不可用。然而,ASN。1原始收据有效负载的格式应该包含它们(即,将唯一性附加到收据的属性)。具体来说,苹果表示要使用收据中提供的设备GUID哈希作为验证点。这应包括在ASN中。1个收据,但不在其JSON转换中。

    (我相信你已经看过了,但以防万一) Apple's receipt validation guide

    相信 如果您只维护了精简的JSON服务器端,那么您就有点运气不好了。当然,我很想被证明是错的。

        3
  •  0
  •   Rashed    7 年前

    根据苹果的文档,您的答案将被找到 here

    此代码将验证应用程序收据-

     func validateAppReceipt(_ receipt: Data) {
        let base64encodedReceipt = receipt.base64EncodedString()
        let requestDictionary = ["receipt-data":base64encodedReceipt]
        guard JSONSerialization.isValidJSONObject(requestDictionary) else {  print("requestDictionary is not valid JSON");  return }
        do {
            let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
            let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt"  // this works but as noted above it's best to use your own trusted server
            guard let validationURL = URL(string: validationURLString) else { print("the validation url could not be created, unlikely error"); return }
            let session = URLSession(configuration: URLSessionConfiguration.default)
            var request = URLRequest(url: validationURL)
            request.httpMethod = "POST"
            request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
            let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
                if let data = data , error == nil {
                    do {
                        let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
                        print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
                        // if you are using your server this will be a json representation of whatever your server provided
                    } catch let error as NSError {
                        print("json serialization failed with error: \(error)")
                    }
                } else {
                    print("the upload task returned an error: \(error)")
                }
            }
            task.resume()
        } catch let error as NSError {
            print("json serialization failed with error: \(error)")
        }
    } 
    

    请求应用商店进行验证的好处是,它会用数据进行响应,您可以轻松地将这些数据序列化为JSON,并从中提取所需密钥的值。无需加密。

    正如苹果在该文档中所描述的

    device -> your trusted server -> app store -> your trusted server -> device
    
        4
  •  0
  •   Oleksandr_DJ    6 年前

    按内容检查收据的唯一性有什么错?例如,将付款收据的MD5哈希附加到适当的用户。此字段在数据库中必须是唯一的。通过这种方式,您可以轻松地检测到重复项。