为什么使用应用内结算设置开发人员有效负载很重要?


78

我正在使用应用内结算API的版本3。我有一个受管理的非消耗性物品。我尚未在我的应用程序中发布此功能,因此我想在购买之前确定购买的有效载荷内容。

来自“安全最佳实践”

发出购买请求时设置开发人员有效负载字符串

使用应用内结算第3版API,在将购买请求发送到Google Play时,您可以包括“开发人员有效载荷”字符串令牌。通常,这用于传递唯一标识此购买请求的字符串令牌。如果您指定字符串值,则Google Play会返回此字符串以及购买响应。随后,当您对此次购买进行查询时,Google Play会返回此字符串以及购买详细信息。

您应该传递一个字符串令牌,该令牌可以帮助您的应用程序识别进行购买的用户,以便以后可以验证该用户是否进行了合法购买。对于消耗品,您可以使用随机生成的字符串,但是对于非消耗品,则应该使用唯一标识用户的字符串。

当您从Google Play取回响应时,请确保验证开发人员有效负载字符串与您先前与购买请求一起发送的令牌匹配。作为进一步的安全预防措施,您应该在自己的安全服务器上执行验证。

对与错,我决定采取设置服务器以进行购买验证的“进一步的安全预防措施”。而且我不会存储自己的购买记录-我总是调用计费API。那么,我真的有理由进行此有效负载验证吗?验证API本身在举报购买商品之前肯定会验证用户的身份,并且如果攻击者破坏了设备(应用程序或Google Play API),我看不出对其进行额外检查会没有任何好处用户在设备上可以轻易绕过的身份。还是有我没有想到的这样做的理由?

Answers:


70

如果您不保留记录,则无法验证您所收到的是您发送的。因此,如果您将某些内容添加到开发人员有效负载中,则可以信任它是合法的(如果签名经过验证,这是一个合理的假设),也可以不完全信任它,而仅将其用作引用,而不用于验证许可证状态,等等。例如,如果存储用户电子邮件,则可以使用该值而不是要求他们再次输入它,这对用户更友好,但是如果应用程序不存在,则应用程序不会中断。

就我个人而言,我认为整个“最佳实践”部分令人困惑,并试图使您完成API真正应该做的工作。由于购买是与Google帐户绑定的,并且Play商店显然会保存此信息,因此他们应该在购买详细信息中提供此信息。获取正确的用户ID需要其他权限,您无需添加其他权限即可弥补IAB API的不足。

因此,简而言之,除非您拥有自己的服务器和特殊的附加逻辑,否则请不要使用开发人员有效负载。只要IAB v3 API可以工作就可以了(不幸的是,这时很大的'if')。


4
初步回答后一年半,您是否仍然有同样的感觉?从现在开始,我不确定该API是否在幕后进行了任何更改。我不能说我现在信任Google的文档。
Alex Lockwood 2014年

6
我仍然认为IAP应该透明地提供安全性,并且不需要使用开发人员有效负载。也许现在可以了,但是我已经有一段时间没有看过它了(甚至使用过它)。我的应用仍然使用原始IAP,主要是因为它提供了我可以在自己的服务器上跟踪的购买通知,而“新” IAP则没有。
Nikolay Elenkov 2014年

2
我认为当然需要在服务器上进行验证,如果您没有使用某种帐户标识符(您自己的帐户或3-dparty)“登录”的用户,那么V3会非常困难。可能要尝试API V2 ...我们计划在2015年1月27日关闭应用内结算第2版服务,此后用户将不再能够通过版本2 API购买应用内商品和订阅。我们强烈鼓励并建议您在2014年11月之前迁移应用程序以使用版本3 API,以使用户有足够的时间将其应用程序更新到新版本。
整数

@Nikolay Elenkov,您对此有何建议?我同意您的观点,这是Google的最佳做法。
stickedoverflow16年

30

您应该传递一个字符串令牌,以帮助您的应用程序识别进行购买的用户...

如果您的应用程序提供了自己的用户登录名和身份(与手机所连接的Google帐户不同),则您需要使用开发者有效负载将购买的商品附加到进行购买的其中一个帐户中。否则,有人可以在您的应用程序中切换帐户,并从购买的东西中受益。

例如

假设我们的应用程序已经登录了userA和userB。手机的Android google帐户是X。

  1. userA,登录我们的应用并购买终身会员。购买详细信息存储在Google帐户X下。
  2. userA注销,而userB登录到我们的应用程序。现在,由于Android google帐户仍为X,因此userB还可以享受终身会员资格。

为避免这种滥用,我们会将购买绑定到一个帐户。在上面的示例中,我们将在userA进行购买时将开发人员有效载荷设置为“ userA”。因此,当userB登录时,有效负载将与登录的用户(userB)不匹配,我们将忽略购买。因此,用户B无法获得用户A进行购买的好处。


好答案。但是,如果我们没有任何使用登录机制,该怎么办?那么我们只是不使用有效载荷?
塔斯玛尼亚克

7
关于这个用户ID问题,我摸了一下头之后,我认为您所指的可能是有效负载字符串的实际意图。Google文档中对此的解释非常简单。
Pooks

因此,用户gmail将发挥作用,并且可以用作developerPaylod
Sami Eltamawy,2014年

3
@keybee这就是重点。在您提到的情况下,如果我们不使用dev负载,则当应用内结算返回“您已经拥有此商品”时,我们将不得不为userB提供高级功能(因为我们无法知道是否是userB造成了首先购买)。如果userB必须在与userA购买同一台设备上进行购买,则userB必须切换到自己的Google帐户并进行购买。
therealsachin

3
@therealsachin我知道了,但是如果我在服务器上检查并阻止userB,他将没有机会购买另一个帐户的会员资格,因为google play首先使用主要帐户进行检查,因此同一设备上的2个用户不会工作。但是,这仅在由于某种原因userA需要一个帐户时才是问题。他将永远无法使用相同的Google帐户再次进行购买,即使使用不同的有效负载也是如此。-对我来说,理想的情况是,在有了新的有效负载的情况下,我应该能够再次购买订阅。
keybee

21

开发人员有效载荷处理还有另一种方法。正如Nikolay Elenkov所说的那样,需要用户ID并为您的应用程序的用户配置文件设置其他权限的开销太大,因此这不是一个好方法。因此,让我们看看Google在In-App Billing v3示例中最新版本的TrivialDrive示例应用中所说的内容:

  • 警告:开始购买时在本地生成随机字符串并在此处进行验证似乎是一种好方法,但是如果用户在一个设备上购买商品然后在其他设备上使用您的应用程序,则此操作将失败。另一台设备将无法访问您最初生成的随机字符串。

因此,如果您要在另一台设备上验证购买的商品,则随机字符串不是一个好主意,但是他们仍然说这不是验证购买响应的好主意。我会说-使用开发人员有效负载仅用于通过发送随机唯一字符串来验证购买,将其保存在首选项/数据库中,并在购买响应中检查此开发人员有效负载。至于在“活动开始”时查询广告资源(应用内购买)的原因-不要麻烦检查开发人员的有效负载,因为这可能会在没有存储该随机唯一字符串的另一台设备上发生。我就是这样看的。


4
谢谢,我正在实现这种方法(我赞成:),因为这听起来像是一个很好的折衷方案,但是我认为所描述的逻辑存在问题。如果购买的有效负载验证失败,则Google Play仍然认为您是该商品的所有者,因此您只需重新启动该应用程序,它将读取库存,而无需进行有效负载验证即可为您提供该商品。一种解决方法是,如果购买的有效载荷验证失败,则将消耗该物品,但是您已经夺走了用户的钱,如果这天真地发生,可能会导致发出讨厌的电子邮件……
Georgie 2013年

如果提供设备ID作为有效负载,则可以实现“按设备许可证”销售。因此,如果用户在一个设备上进行了购买,他将无法在另一设备上使用它。这对于销售完整版本的应用程序f.ex可能很有用。防病毒软件。
bancer 2013年

阅读@Georgie评论。否则,您将花费大量时间弄清楚这是一个坏主意。
Halvor Holsten Strand

16

这取决于您如何验证developerPayload。有两种方案:远程验证(使用服务器)和本地验证(在设备上)。

服务器

如果您使用服务器进行developerPayload验证,那么它可以是任意字符串,可以在设备和服务器上轻松计算。您应该能够识别执行请求的用户。假设每个用户都有对应的accountId,则developerPayload可以将其计算为与purchaseId(SKU名称)的组合,例如:

MD5(purchaseId + accountId)


设备

developerPayload 不应是用户电子邮件。为什么不应该使用电子邮件作为有效负载的一个很好的例子是Google for Work服务。用户可以更改其与该帐户关联的电子邮件。唯一不变的是accountId。在大多数情况下,电子邮件可以接受(例如,目前Gmail地址是不可变的),但请记住要为将来设计。

多个用户可能使用同一台设备,因此您必须能够区分谁是商品的所有者。对于设备验证,developerPayload是一个唯一标识用户的字符串,例如:

MD5(purchaseId + accountId)


结论

通常developerPayload,两种情况下的都可能只是accountId。对我来说,这似乎是默默无闻的安全感。MD5(或其他哈希算法)purchaseId只是使有效负载更加随机的一种方式,而无需明确表明我们使用的是帐户ID。攻击者必须反编译应用程序才能检查其计算方式。如果您对应用程序感到困惑,那就更好了。

有效负载不提供任何安全性。可以很容易地用“设备”方法欺骗它,而无需花费任何精力在“服务器”检查中。请记住使用Google Publisher帐户控制台中提供的公共密钥来实施签名检查。

*有关使用帐户ID(而非电子邮件)的必读博客文章。


1
什么是accountId?&如何获取accountId?能否为多个Google Play帐户(同时切换帐户)维护相同的accountId以获得更多详细信息,请访问github.com/googlesamples/android-play-billing/issues/2
Sarath Kumar,

5

在琐碎的驱动器示例作者自己提供的有关IAB v3的Google IO视频中,在视频结尾处对此进行了简要介绍。这是为了防止重放攻击,例如,攻击者嗅探流量,窃取包含成功购买的数据包,然后尝试在自己的设备上重放数据包。如果您的应用在发布高级内容(理想情况下也来自服务器)之前没有通过dev有效负载(最好在服务器上)检查购买者的身份,攻击者将成功。由于数据包完好无损,因此签名验证无法检测到。

在我看来,这种保护似乎对于具有在线帐户连接性(例如部落冲突)的应用程序是非常理想的(有效载荷是自然产生的,因为无论如何您都必须识别用户),尤其是在黑客行为破坏多人游戏玩法的同时,除了简单的本地化盗版案例以外,其影响深远。相反,如果apk上的客户端黑客已经可以解锁高级内容,则此保护不是很有用。

(如果攻击者试图欺骗有效载荷,则签名验证应该会失败)。


我可以将Firebase与Google登录一起使用,以及将account_id,developer_payload字段存储在实时数据库中的实时数据库,并在用户打开应用程序时对其进行检查(对于离线功能具有其他共享首选项)?
Dhaval Jotaniya '18

5

2018年末更新:官方Google Play计费库有意不公开developerPayload。从这里

字段developerPayload是一个旧字段,保持与旧实现的兼容性,但正如“购买应用内结算产品”页面(https://developer.android.com/training/in-app-billing/purchase-iab -products.html),在完成与应用内结算相关的任务时,此字段并非始终可用。并且由于该库旨在代表最新的开发模型,因此我们决定在实现中不支持developerPayload,并且我们也没有计划将该字段包含在库中。

如果您将任何重要的应用内结算逻辑实现依赖于developerPayload,我们建议您更改此方法,因为此字段在某个时候(或很快)将被弃用。推荐的方法是使用自己的后端来验证和跟踪有关订单的重要详细信息。有关更多详细信息,请查看“安全和设计”页面(https://developer.android.com/google/play/billing/billing_best_practices.html)。


这为我解决了这个问题……它已被弃用,因此不应使用
smitty1

3

我为此苦苦挣扎。由于Google Play帐户只能拥有任何“托管”项目中的一个,但是可以拥有多个设备(我拥有三个设备),因此,某人出售“每台设备”的上述评论将不起作用...能够将其放到他们的第一台设备上,再也没有其他任何设备上……如果您购买高级升级,则它应该可以在所有手机/平板电脑上使用。

我鄙视获取用户电子邮件地址的概念,但是我真的没有找到其他可靠的方法。因此,我在帐户列表中抓取了一个与“ google.com”匹配的第一个帐户(是,添加到清单的权限),然后立即对其进行哈希处理,因此它不再可用作电子邮件地址,但确实提供了“足够唯一”令牌。这就是我作为开发人员有效负载发送的内容。由于大多数人都使用其Google Play ID激活其设备,因此很不错,这三台设备都将获得相同的令牌(在每台设备上使用相同的哈希算法)。

它甚至可以在具有多个“用户”的KitKat上使用。(我的开发人员ID在一个用户上,我的测试ID在另一个用户上,每个用户都在自己的沙箱中)。

我已经在总共3个用户的六台设备上对它进行了测试,并且每个用户设备都返回了相同的哈希值,并且不同的用户都具有不同的哈希值,满足了准则。

我从来没有存储用户的电子邮件地址,它是直接从代码中传递的,以将帐户名传递给哈希函数,并且仅哈希存储在堆中。

可能还有更好的解决方案,可以更加尊重用户的隐私,但是到目前为止,我还没有找到它。应用发布后,我将在隐私权政策中对如何使用用户的电子邮件地址进行非常清晰的描述。


6
很好,但是需要ACCOUNTS权限,这在这里是不必要的。为什么Google在地球上无法对用于进行购买的帐户进行哈希处理……这将是最干净的解决方案。现在,它只是普通的“损坏”。
整数

我做了类似的事情,但是现在,对于Android 6,该应用程序必须要求用户授予“联系人”权限,这对于用户首次启动该应用程序来说是一种糟糕的体验。也许我将改为实施“还原购买”选项。另外,如果您抢到第一个google.com帐户,并且用户的手机上有多个帐户并重新排列帐户,则可能会遇到问题(例如,删除帐户A,然后再添加一次,但现在不是第一个帐户) 。尽管确实很少,但确实发生了。因此,您还需要使用帐户选择器!ch!
snark

1

这通常会响应产品定义(您的应用程序)。例如对于订阅的情况。同一用户将能够在他/她拥有的所有设备上使用订阅吗?如果答案是肯定的。我们没有检查有效载荷。

用于消耗品。假设您在应用程序中购买了10个虚拟硬币。用户能否在不同设备上使用这些硬币?一台设备上为4,另一台设备上为6?如果我们只想在进行购买的设备上工作,则必须检查有效负载,例如使用自生成的字符串并本地存储。

基于这些问题,我们必须决定如何实施有效负载检查。

问候

圣地亚哥

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.