防止重播攻击appStoreReceiptURL应用收据

时间:2018-04-17 04:57:52

标签: ios app-store storekit receipt-validation

我们只提供服务器端服务,我们只想提供给付费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": []
}

我担心此收据的重播攻击。在重放攻击中,一个设备购买应用程序并提交有效收据,但第二个未授权设备存储并发送第一个收据的精确副本。由于第一张收据是由Apple签署的,因此副本似乎有效。

理想情况下,我们通过观察收据中的唯一标识符来击败重播攻击;如果有人试图重新发送相同的收据ID,我们就会知道它是重复的。出于这个原因,IAP收据包含transaction_identifier字段。

但似乎没有唯一标识符可用于识别付费应用收据的重播攻击。黑客可以从不同的设备向我们转发此收据,我们无法知道它是重复的收据还是新的原始收据。

话说回来之后,我的目标是沙箱收据中的_id个数字为adam_idapp_item_iddownload_idrootViewController。我们可以使用其中任何一种来识别重复的收据吗?还是有其他一些更好的处理方法吗?

4 个答案:

答案 0 :(得分:1)

无法检测付费应用的重复收据。 adam_idapp_item_iddownload_id没有文档记录,因此开发人员不能依赖它们来达到安全目的。这与IAP收据不同,后者包括可以进行重复数据删除的transaction_identifier

但是,有一种可能的解决方法。您可以为用户提供价格为0美元(免费)的非消耗性IAP,并要求用户购买"购买"它是为了访问服务器端功能。由于它是非易耗品IAP,因此每位付费应用用户最多只能购买一次。

用户同意免费购买"这有点麻烦。并登录App Store进行访问,但一旦他们这样做,他们就会拥有与付费应用收据相关联的IAP。免费的IAP收据包括交易标识符;服务器可以使用事务ID对重复购买进行重复数据删除。

答案 1 :(得分:0)

据我所知,您收到的唯一标识符在收据的JSON格式中无法使用。但是,原始收据有效载荷的ASN.1格式应该具有它们(即附加收据唯一性的属性)。具体来说,Apple表示要使用收据中提供的设备GUID哈希作为验证点。这应包含在ASN.1收据中,但不包含在其JSON转换中。

(我确定你已经看过这个,但以防万一) Apple's receipt validation guide

相信如果您维护服务器端的所有内容都是精简的JSON,那么您运气不好。当然,爱被证明是错的。

答案 2 :(得分:0)

根据Apple文档,您的答案将会找到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的数据进行响应,并从中拉出所需键的值。无需加密。

正如Apple在该文档中所描述的那样

device -> your trusted server -> app store -> your trusted server -> device

答案 3 :(得分:0)

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