我的服务器如何安全地验证 iPhone 应用内购买?

Posted

技术标签:

【中文标题】我的服务器如何安全地验证 iPhone 应用内购买?【英文标题】:How can my server securely authenticate iPhone in-app purchase? 【发布时间】:2010-12-07 13:32:42 【问题描述】:

查看 Apple 的 server purchase model 图表。

在第 9 步中,服务器如何知道它真的在与有权购买的 iPhone 通话,并且 Eve 没有使用不诚实获得的收据进行重放?

收据可能有效,但这并不能证明发件人是受权方。

iPhone 上是否有任何设备证书概念可用于签署收据?

有什么方法可以将收据绑定到设备上,或者将收据绑定到iTunes账号和设备上,这样服务器就可以验证了吗?

【问题讨论】:

你提交过苹果的错误吗? 不 - 有应用程序吗? ;-)。说真的,他们会认真对待吗? 苹果错误报告是苹果工程师与世界沟通的方式。他们查看具有大多数重复项的错误,以确定他们工作的优先级。所有与我交谈过的苹果工程师都要求我提交错误。 【参考方案1】:

Apple 提供的漏洞方法

服务器可以通过doing the following验证购买:

    iPhone 应用程序在购买后收到transactionReceipt。让 iPhone base64 对其进行编码(您可以使用这个开源的addition to NSData) 并将其发送到您的服务器。 (您甚至可以按原样发送,并在验证之前让服务器对其进行 base64 编码。)

    让您的服务器使用 HTTP POST 发送一个 JSON 请求,其中包含单个密钥 receipt-data 以及 base64 编码的 transactionReceipthttps://buy.itunes.apple.com/verifyReceipt。 (有关如何以各种服务器端语言执行此操作的说明see this site)

    服务器将响应一个带有两个键的 JSON 对象:status 是一个整数,receipt 是重复的收据。

如果状态为零,则应接受有效收据,非零值表示收据无效。

Apple 方法的安全补充

但是,存在一些安全隐患。用户可以使用另一个用户的收据,因为设备没有绑定到收据,或者用户可以使用另一个产品的收据,因为服务器不验证收据的产品 ID。为确保不会发生这种情况,您还应该执行以下操作:

    当您第一次在应用程序中获得收据时,立即通过安全通道(例如 HTTPS 或 SSL 套接字)将其与 device's UUID 一起发送到您的服务器。 不要存放在任何地方,留在内存中。

    在您的服务器上,将 UUID 和收据对存储在数据库中。

    当设备发送 UUID 和收据对时,请使用您的数据库验证收据尚未被使用,通过检查 @ 确保收据实际上是您的产品987654328@。 收据只是一个 JSON 对象,因此您的服务器可以通过从 base64 解码收据来读取内容。

    通过安全通道向设备返回响应,告诉它是否购买:

    认证为新的(不在数据库中并且有效) 过去已通过身份验证(相同的 UUID 和收据对已在数据库中) 由于产品 ID 错误而被拒绝 由于已将收据与另一个 UUID 一起使用而被拒绝。

由于收据只存在于设备的内存中,并且您的应用程序使用设备的 UUID(可以是 spoofed by jailbroken devices,请参阅 cmets),并且您的所有产品购买都使用以安全的方式在您的服务器上存储设备的 UUID;用户不能使用其他用户的收据来验证购买,他们也不能使用其他产品的收据,因为您会检查它。

如果您想验证交易的其他详细信息,还可以验证收据中的其他字段。例如,如果您的产品是订阅产品,您还想查看交易日期。

此外,用户不能通过将设备放在与您的主机同名的专用网络上来伪装成您的服务器,因为他们没有您的 SSL 证书。

失败注意事项

由于在用户的设备获取收据和与您的服务器验证之间可能会发生故障(例如,如果用户失去连接,或者您的服务器因维护而停机),您还应该让用户“重新授权” .重新授权应该从商店获取收据(使用Restored Transaction) 并将其重新发送到服务器,就像这是新购买一样。这应该很少需要使用,但应该可以避免用户在网络故障的情况下重新购买产品。

多设备注意事项

这意味着如果用户想要在多个设备上使用应用程序,他们将不得不多次购买该产品。这可能是您想要的效果,但您可能应该在用户购买之前通知他们,因为他们可能希望能够在与他们的帐户关联的设备上使用这些内容。

如果收据还包含 iTunes 帐户信息,则身份验证可以使用该信息来允许用户在其所有设备(但不是他们的朋友)之间共享内容。

【讨论】:

我不确定我是否正确理解您使用设备 UUID 的方法。我的理解是,您可以在多个设备上安装应用程序而无需额外费用。此外,将应用程序限制为设备 UUID 意味着如果您升级/更换手机,您需要重新购买所有应用程序。 @kristof:你是对的,但这是安全验证收据的唯一方法,因为它不能被欺骗。如果 Apple 在收据中包含 iTunes 帐户,或者如果有一种安全方法来验证给定设备 UUID 是否与另一台设备关联到同一个 iTunes 帐户,则您可以在与给定 iTunes 帐户关联的所有设备上安全地验证购买,但是事实并非如此。也许在未来的更新中,这将可用。 @jdandrea:你是对的:“如果用户尝试购买他们已经购买的非消耗性物品,您的应用程序会收到该物品的常规交易,而不是恢复交易。但是,用户不会对该产品再次收费。您的应用程序应将这些交易视为与原始交易相同的交易。”来源:developer.apple.com/iphone/library/documentation/… 正确-因此问题-如何验证设备:-)。 UDID 对我们没有任何帮助。 UDID 可以在没有越狱设备的情况下被欺骗。作为服务器,您不知道移动设备上实际运行的是什么代码。它可以发送任何它想要的 UDID 值。【参考方案2】:

我不认为可以将收据绑定到设备。

我的理解是,您可以在多台设备上安装应用程序而无需额外费用。将其绑定到设备意味着如果您升级/更换手机,您将需要重新购买所有应用程序。

【讨论】:

问题是,我无法限制设备的数量,因为我无法进行身份验证。如果收据在野外,1000 台设备可以提取内容。 Apple 支持“消耗性”和“非消耗性”应用内购买。提供“消耗性”产品绝对有效,该产品仅在特定设备上有效,而在另一设备上无效。这取决于我们的业务需求,也就是您向客户销售的产品【参考方案3】:

我相信,如果您无法读取用户的 Apple ID,那么您对盗版的唯一保护就是跟踪(当然是服务器端)每个 transaction_id 的下载请求数量,并在它们超过某个值时限制它们.

因此,如果您将其限制为 50,则为用户在多个设备上部署应用程序及其内容并多次恢复提供了合理的余地,但对于想要分发具有有效无限制恢复的收据。当然,他们可以只分发包含您所有内容的版本,但您对此无能为力,至少他们不会对您的服务器征税。

【讨论】:

【参考方案4】:

UDID 不再工作

Beniot 的回答很棒,但是,正如 Joe D'Andrea 所提到的,这些天来,UDID 已被弃用,我上次尝试时,使用调用获取 UDID 的应用程序在上传到 iTunes 期间未能通过验证。

限时收据替代收据计数器

要补充 hloupyhonza 的答案,除了为特定收据设置“下载请求”计数器外,您还可以按时间限制收据的有效性。我发现 12 到 24 小时之间的任何时间都是合理的。

此方法还允许购买者在他拥有的任何其他设备上使用购买,只要他使用相同的 Apple ID 登录 App Store。注意:每次恢复购买完成后,Apple 都会返回一张全新的收据(包含原始收据的详细信息) - 这允许在我们为特定收据设置的时间限制之后恢复购买。

防止“现成的”黑客攻击

为了防止典型的“Googled”黑客攻击解决方案(我的数据显示这构成了几乎所有 IAP 黑客攻击尝试),我使用了以下校验和(选择你最喜欢的算法,没关系,除非你想让它无懈可击)串联:

收据数据json字符串 共享密钥 验证成功状态码。

应用程序将验证我们的验证服务器返回的校验和。但这并不是无懈可击的,因为黑客可能会从您的应用程序的二进制文件中检索共享密钥。但到目前为止,它已经阻止了所有“现成的”黑客攻击,这对我来说已经足够了。

【讨论】:

以上是关于我的服务器如何安全地验证 iPhone 应用内购买?的主要内容,如果未能解决你的问题,请参考以下文章

Android应用内购买服务器签名验证使用php OpenSSL

在报亭中处理应用内购买

Firebase Firestore +云功能服务器端验证应用内购买收据(Swift + NodeJS)

我们如何在服务器端验证 Android 应用内计费收据?

Firebase iOS SDK 如何执行其应用内购买验证?

如何验证应用内购买的 Android 服务器端 Java?