防止重放攻击 appStoreReceiptURL 应用收据
Prevent replay attacks appStoreReceiptURL app receipts
我们有一项服务器端服务,我们只想向付费 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_id
、app_item_id
和 download_id
。我们可以使用其中任何一个来识别重复收据吗?或者有其他更好的处理方法吗?
据我所知,您要查找的唯一标识符在收据的 JSON 格式中不可用。但是,原始收据有效负载的 ASN.1 格式应该具有它们(即,将唯一性附加到收据的属性)。具体来说,Apple 表示使用收据中提供的设备 GUID 哈希作为验证点。这应该包含在 ASN.1 收据中,但不包含在其 JSON 转换中。
(我相信你已经看到了,但以防万一)
Apple's receipt validation guide
我相信如果您维护的所有服务器端都是精简版JSON,那您就有点不走运了。当然,希望被证明是错误的。
根据 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
无法检测付费应用的重复收据。 adam_id
、app_item_id
和 download_id
没有记录,因此开发人员不能出于安全目的依赖它们。这与 IAP 收据不同,IAP 收据包含可以删除重复数据的 transaction_identifier
。
不过,有一个可能的解决方法。您可以向用户提供成本为 0 美元(免费)的非消耗性 IAP,并要求用户 "buy" 它才能访问服务器端功能。由于是非消耗型内购,每个付费应用用户最多只能购买一次。
用户同意免费 "purchase" 并登录 App Store 以访问它有点麻烦,但一旦他们这样做,他们将拥有一个与他们的付费绑定的 IAP - 应用收据。免费的 IAP 收据包括交易标识符;服务器可以使用交易 ID 对购买进行去重。
通过内容检查收据的唯一性有什么错?例如,将付款收据的 MD5 散列附加到适当的用户。该字段在您的数据库中必须是唯一的。通过这种方式,您可以轻松检测到重复项。
我们有一项服务器端服务,我们只想向付费 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_id
、app_item_id
和 download_id
。我们可以使用其中任何一个来识别重复收据吗?或者有其他更好的处理方法吗?
据我所知,您要查找的唯一标识符在收据的 JSON 格式中不可用。但是,原始收据有效负载的 ASN.1 格式应该具有它们(即,将唯一性附加到收据的属性)。具体来说,Apple 表示使用收据中提供的设备 GUID 哈希作为验证点。这应该包含在 ASN.1 收据中,但不包含在其 JSON 转换中。
(我相信你已经看到了,但以防万一) Apple's receipt validation guide
我相信如果您维护的所有服务器端都是精简版JSON,那您就有点不走运了。当然,希望被证明是错误的。
根据 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
无法检测付费应用的重复收据。 adam_id
、app_item_id
和 download_id
没有记录,因此开发人员不能出于安全目的依赖它们。这与 IAP 收据不同,IAP 收据包含可以删除重复数据的 transaction_identifier
。
不过,有一个可能的解决方法。您可以向用户提供成本为 0 美元(免费)的非消耗性 IAP,并要求用户 "buy" 它才能访问服务器端功能。由于是非消耗型内购,每个付费应用用户最多只能购买一次。
用户同意免费 "purchase" 并登录 App Store 以访问它有点麻烦,但一旦他们这样做,他们将拥有一个与他们的付费绑定的 IAP - 应用收据。免费的 IAP 收据包括交易标识符;服务器可以使用交易 ID 对购买进行去重。
通过内容检查收据的唯一性有什么错?例如,将付款收据的 MD5 散列附加到适当的用户。该字段在您的数据库中必须是唯一的。通过这种方式,您可以轻松检测到重复项。