防止重放攻击 appStoreReceiptURL 应用收据
Posted
技术标签:
【中文标题】防止重放攻击 appStoreReceiptURL 应用收据【英文标题】:Prevent replay attacks appStoreReceiptURL app receipts 【发布时间】:2018-09-26 22:54:49 【问题描述】:我们有一项服务器端服务,我们只想向付费 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
字段。
但似乎没有唯一标识符可用于识别带有付费应用收据的重放攻击。黑客可以从不同的设备将这张收据重新发送给我们,我们无法知道它是重复的收据还是新的原始收据。
话虽如此,我的目光还是被那些在沙盒收据中为 0 的 _id
所吸引:adam_id
、app_item_id
和 download_id
。我们可以使用其中任何一个来识别重复收据吗?还是有其他更好的方法来处理这个问题?
【问题讨论】:
这有帮助吗? ***.com/questions/17669747/… 和 futurice.com/blog/validating-in-app-purchases-in-your-ios-app 据我所见。问题是有一种很好的方法可以防止针对 IAP 的重放攻击(检查交易标识符),但对于付费应用程序却没有。那里的链接讨论保护 IAP,而不是应用程序。 是的,理论上重放攻击是可能的,但我不认为这值得担心太多。为了成功,攻击者需要一张有效的收据和一种将其放置在他的设备上正确位置的方法,以便您的应用程序可以找到和使用它。这当然是可能的,但对于普通用户来说很复杂。因此,只有真正想要破坏您的系统的攻击者才会尝试这种方法。这些攻击者中有多少存在,如果他们成功了,是否会对您的业务造成任何损害?可能发生的最糟糕的情况是,一群人在不付费的情况下使用您的应用。 收据base64字符串可以从POST请求中提取,如果你将它发送到你的服务器 【参考方案1】:无法检测付费应用的重复收据。 adam_id
、app_item_id
和 download_id
未记录在案,因此开发人员不能出于安全目的依赖它们。这与 IAP 收据不同,IAP 收据包含可重复数据删除的transaction_identifier
。
不过,有一个可能的解决方法。您可以为用户提供 0 美元(免费)的非消耗性 IAP,并要求用户“购买”它以访问服务器端功能。由于它是非消耗性 IAP,每个付费应用用户最多可以购买一次。
用户同意免费“购买”并登录 App Store 访问它有点麻烦,但一旦他们这样做,他们将拥有一个与付费应用收据绑定的 IAP。免费 IAP 收据包括交易标识符;服务器可以使用交易 ID 对购买进行重复数据删除。
【讨论】:
【参考方案2】:据我所知,您要查找的唯一标识符在收据的 JSON 格式中不可用。但是,原始收据有效负载的 ASN.1 格式应该具有它们(即,将唯一性附加到收据的属性)。具体来说,Apple 表示使用收据中提供的设备 GUID 哈希作为验证点。这应该包含在 ASN.1 收据中,而不是其 JSON 转换中。
(我相信你已经看过这个,但以防万一) Apple's receipt validation guide
我相信如果您在服务器端维护的只是精简的 JSON,那么您有点不走运。当然,希望被证明是错误的。
【讨论】:
我认为这不适用于服务器端验证。 GUID 哈希基于UIDevice identifierForVendor,“当用户从设备中删除该供应商的所有应用程序并随后重新安装其中一个或多个应用程序时,它会发生变化。”用户可以删除并重新安装该应用,以生成无限数量的具有不同 GUID 的签名收据。 啊,对,太对了,太对了。 最好的方法是将收据 base64 字符串转换为 MD5 哈希并将其存储在您的数据库中并使用它来检查未来的重复...这样即使您的 POST 请求被拦截并发送出去它也会仍然被拒绝..【参考方案3】:根据 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 的数据,并从那里提取您想要的键的值。无需加密。
正如苹果在该文档中描述的那样
device -> your trusted server -> app store -> your trusted server -> device
【讨论】:
感谢您的来信;我已经更新了我的问题以澄清。我专门针对“重放攻击”,其中一个设备购买了应用程序,然后第二个未经授权的设备尝试向我发送与第一个设备完全相同的收据。第一个收据是有效的,因此第二个重新传输的收据对 Apple 来说似乎是有效的。我需要一种方法来检测第二张收据是重复收据。 感谢您的更新。你可以看到这个链接..可能对你的问题有帮助。链接-github.com/crashoverride777/swifty-receipt-validator/blob/…。您需要先检测到未经授权的设备,然后再尝试验证。 链接不再有效@Md Rashed Pervez developer.apple.com/documentation/storekit/in-app_purchase/… - @MaksimKniazev @DanFabulich “第二台未经授权的设备尝试向我发送与第一台设备完全相同的收据”您可以通过执行额外的设备上收据验证来防止这种情况发生,该验证检查收据是否为当前设备。【参考方案4】:通过内容检查收据的唯一性有什么问题?例如,将付款收据的 MD5 哈希附加到适当的用户。此字段在您的数据库中必须是唯一的。通过这种方式,您可以轻松检测到重复项。
【讨论】:
好主意以上是关于防止重放攻击 appStoreReceiptURL 应用收据的主要内容,如果未能解决你的问题,请参考以下文章