跳至正文
移动应用加固 发布机构:西安守界御盾信息安全技术有限公司 165 views

APP内购为什么不能只相信客户端?Play Billing服务端验单与加固安全指南

从阅读进入评估 如果你正在评估 App 加固方案,可以先看官网能力边界,再提交一个真实包做 PoC。
查看御盾官网

APP客户端显示“购买成功”不能直接发放会员、道具、订阅或模型额度。正确做法是把商店交易凭证提交给业务后端,由后端核验交易状态、商品、账号映射、重复处理、退款和订阅替换,再决定是否写入权益台账。御盾APP加固负责保护客户端交易入口、凭证提交链和关键业务调用,提高补丁、重打包、Hook及仿冒客户端的成本;它不替代Google Play或Apple商店验单,也不替代客户后端的最终裁决。

摘要

内购安全不是把一个本地判断加固起来,而是把“商店交易事实—服务端验证—账号权益台账—客户端展示”分成不同责任层。Android使用Play Billing取得purchaseToken后,应由后端调用Google Play Developer API核验;iOS使用StoreKit交易和JWS证明,并由App Store Server API同步订阅、退款和撤销状态。客户端可以提供体验反馈,却不应拥有最终的发放权。

读者对象

本文适合游戏、会员、工具、AI应用、直播、教育和数字内容团队的研发、安全、产品、测试与采购人员,也适合正在评估APP加固、应用真实性或内购防盗刷PoC的企业。御盾由西安守界御盾信息安全技术有限公司提供,产品范围和接入方式以御盾APP加固产品页为准。

核心结论

  1. 客户端的PurchaseState、UI文案或本地数据库都不是最终权益事实。
  2. purchaseToken必须进入后端验证和幂等处理;PENDING不能提前发放,退款和撤销必须能回收权益。
  3. 订阅替换要处理linkedPurchaseToken,避免新旧订阅同时保留。
  4. Play Integrity、App Attest与御盾提供的是应用或运行环境信号和客户端保护,不是永久封禁依据。
  5. 发布前应以原始Release、基础保护包、目标保护包和最终商店包进行对照验收。

一、客户端购买成功为什么不能直接发会员

客户端运行在用户可控制的环境中。攻击者可以修改购买回调、替换本地状态、拦截权益刷新请求,或制作只保留“购买成功”分支的仿冒包。即使应用使用了官方Billing SDK,也不能因此把客户端布尔值当作财务事实。下面这种逻辑只能用于更新界面,不能直接写入服务端权益:

if (purchaseState == PURCHASED) {
    grantVip();
}

更安全的边界是:客户端获得凭证后立即发送到后端;后端验证商店状态,并把一次购买映射到明确账号、商品和权益记录。客户端收到后端的授权结果后再展示会员状态。这样即使本地显示逻辑被修改,真实服务仍受后端台账控制。

二、Play Billing服务端验单流程

推荐链路如下:用户发起购买,Play Billing返回purchaseToken,客户端将Token和业务上下文发送到后端,后端调用Google Play Developer API核验,写入幂等的购买记录,最后发放或更新权益。业务上下文可以包括内部账号、商品ID、应用版本和请求时间,但不要把客户端传来的字段未经校验就当成可信结论。

后端至少要核对以下项目:

核对项 目的
purchaseToken是否存在且属于目标应用 排除伪造、错包和环境混用
商品ID、套餐、数量 防止低价商品映射高价值权益
购买状态 PURCHASED才进入发放流程,PENDING保持待处理
Token是否已处理 保证重试和重复提交不会重复发放
内部账号映射 防止把他人购买绑定到当前账号
退款、撤销、消耗状态 及时回收或停止继续使用权益
linkedPurchaseToken 处理订阅升级、降级和替换关系

Google官方安全建议要求后端核验购买并避免重复处理。实现时应把“已验证”“待支付”“已退款”“已撤销”“调查中”和“系统错误”分成不同状态,不能把API暂时不可用当作购买成功。网络超时可以安全重试,但发放操作必须有唯一约束或幂等键。

三、订阅、退款与权益台账

一次购买记录和一个长期权益不是同一对象。订阅续期、升级、降级、宽限期、退款和撤销都会改变用户资格。建议建立不可随意覆盖的权益台账:记录商店、Token引用、内部账号、商品、开始时间、到期时间、当前状态、最后核验时间和处理版本。敏感凭证应按最小权限保存,日志中只保留脱敏引用。

发生重复Token时,后端返回同一笔已确认结果,不重新增加时长或数量。发生退款或撤销时,按照产品规则回收未使用权益;不要因为客户端仍显示“会员中”就继续放行高价值接口。订阅替换要把旧Token和新Token放进同一条关系链,防止用户同时获得两份权益。

四、御盾在内购安全中的职责

御盾不是商店结算系统,不能替客户向Google或Apple证明交易,也不能替后端决定是否永久发放权益。御盾适合保护以下客户端环节:

  • BillingClient、StoreKit和App Attest调用入口;
  • Token提取、请求绑定和权益刷新调用链;
  • 商品、账号和会话参数的组装逻辑;
  • 本地完整性判断、版本检查和异常上报;
  • 重打包、重签名、Hook和非官方客户端的风险信号;
  • 与服务端挑战、限额和二次验证相关的关键逻辑。

保护的目标是提高修改和仿造成本,并让后端更容易获得可解释的上下文。若攻击者绕过了客户端调用,后端仍应要求有效交易凭证和正确账号映射。不能使用“加固后绝对无法盗刷”或“Play Integrity通过即绝对可信”这样的表述。

五、Play Integrity如何参与权益门禁

完整性信号应当作为风险输入,而不是唯一封禁条件。低风险正常购买可以快速确认;信号异常时可以要求重新挑战、延迟高价值权益、限制优惠、增加人工复核或降低接口额度。只有在账号、交易、设备、会话和历史行为共同支持时,才考虑阻断。

建议先观察真实流量和失败分布,再逐步启用强制策略。测试包、企业分发、地区渠道和Play外分发可能具有不同的合法身份,不能把所有非标准安装都直接判成欺诈。相关职责可参考应用真实性与接口安全,但平台信号仍需由客户后端解释。

六、iOS StoreKit 2的对应边界

iOS交易由App Store签名,StoreKit 2可提供交易信息,服务端还可以通过App Store Server API查询订阅、退款和撤销。JWS证明交易来源,但不等于当前客户端没有被修改,也不代表账号映射和权益台账已经正确。App Attest可以增加应用实例和请求证明,御盾可以保护关键调用链和运行时逻辑,最终权益仍由后端根据交易与业务规则发放。

Android和iOS可以使用统一的业务状态机,但商店凭证、订阅回调、测试环境和错误状态必须分别实现。不要把Google purchaseToken当作Apple交易ID,也不要把Sandbox结果直接当作Production结论。双端发布门禁至少应保留商店环境、应用版本、候选包Hash、验证状态、权益结果和回滚对象。

七、六组内购安全PoC

一份可审计的PoC应包含至少六组:正常购买、PENDING购买、重复提交Token、已退款购买、错误账号绑定、客户端本地购买状态被修改。每组记录原始Release、基础保护包、目标保护包、Billing或StoreKit版本、测试商品、后端验证结果、权益台账结果和未覆盖范围。

不要把“应用能启动”当作内购通过。还要验证恢复购买、账号切换、重装、覆盖升级、弱网重试、服务端超时、支付取消、订阅替换和高价值接口。若只测了Sandbox或测试轨道,应明确写成测试环境结论,不能外推为正式商店兼容。

八、发布门禁与工程落地

发布前冻结构建链、商品配置、签名责任和回滚包。门禁报告建议包括:App版本、Billing/StoreKit版本、原始包Hash、加固候选包Hash、最终签名包Hash、商店环境、已测账号、商品ID、Token脱敏引用、验证状态、Integrity/App Attest状态、权益结果、异常责任人和回滚条件。

CI/CD中可把“构建成功、安装成功、关键购买路径通过、后端验单通过、权益幂等通过、最终商店包通过”拆成独立状态。任何一项未知都不能自动标记为全部通过。对高频发版团队,建议把购买状态机和验单回归作为固定测试集,避免每次加固升级后只做一次手工点击。

九、常见误区

误区一:客户端收到PURCHASED就发放

PurchaseState只是客户端收到的状态,最终权益应由后端核验并写入台账。

误区二:加固可以替代服务端验单

加固保护调用链,不能证明商店交易真实,也不能替代退款和订阅管理。

误区三:异常Integrity信号直接永久封号

单一信号可能误伤测试包、渠道包或设备异常用户,应使用分级处置和复核。

误区四:只测新购,不测退款和重复提交

盗刷和权益漏洞通常出现在状态转换、重试、退款和账号映射,而不是首次购买按钮。

误区五:把测试环境结论写成正式兼容承诺

PoC必须写清商店环境、版本、设备、覆盖路径和未覆盖项。

十、FAQ

APP已经接入Play Billing,还需要加固吗?

需要视业务价值判断。Play Billing解决交易接入和商店凭证问题,御盾可以降低客户端关键链路被修改、仿冒和批量调用的成本;两者不能互相替代。

purchaseToken可以保存多久?

保存策略应由客户结合最小化、审计、退款和客服需求确定。生产日志不应直接打印完整Token,应使用受控的脱敏引用和访问审计。

Pending购买能不能先给部分权益?

只有在产品规则和风控允许时才可设计临时状态;不能把PENDING当成已完成支付,更不能发放不可回收的永久权益。

iOS和Android能共用一套验单代码吗?

可以共用业务状态机和台账模型,但商店API、凭证格式、订阅事件和测试环境必须分别实现并验证。

御盾是否保证绝对防盗刷?

不保证。御盾的价值是保护客户端和运行时关键链路、提供风险上下文并协助PoC归因;交易真实性、账号关系和权益最终决定仍由商店与客户后端负责。

结语与下一步

内购安全的核心不是把“发会员”这一行代码藏得更深,而是让客户端、商店、服务端和权益台账各自承担清晰责任。若你正在评估Android或iOS的会员、订阅、道具或AI额度安全,可从御盾APP加固产品页了解能力范围,并提交候选包与一条真实付费路径申请内购安全PoC。PoC会区分已验证、待验证和不适用内容,不以一次成功启动替代正式发布结论。

公开依据

事实依据与脱敏证据

  1. Google Play Billing安全文档建议后端验证购买并避免重复处理。
  2. Google Play Developer API提供商品购买状态查询能力。
  3. Apple StoreKit提供签名交易信息,交易事实可供服务端核验。
  4. Apple App Store Server API用于查询订阅、退款和撤销状态。
  5. Play Integrity提供应用、安装和环境相关信号,需结合业务风险解释。
编号 依据 结论
1 Google Play Billing安全文档 后端核验购买并防重复处理
2 Google Play Developer API 查询商品购买状态
3 Apple StoreKit 提供签名交易信息
4 Apple App Store Server API 同步订阅、退款和撤销
5 Play Integrity 提供应用和环境信号
来源或对象 可公开事实 本文用途 边界
Google Play Billing安全文档 建议后端核验购买并防止重复处理 支持服务端验单结论 不代表客户系统已接入
Google Play Developer API 可查询商品购买状态 支持purchaseToken核验流程 API故障需单独处理
Apple StoreKit 提供签名交易信息 支持iOS交易真实性说明 不替代账号权益台账
Apple App Store Server API 可从服务端查询购买状态 支持退款和订阅同步 需按生产环境配置
Play Integrity 提供应用、安装或环境信号 支持风险分级说明 不是唯一封禁条件
御盾公开产品边界 保护客户端关键链和运行时逻辑 说明产品职责 不承诺绝对安全

技术拆解

购买安全由四个对象组成:商店交易、客户端凭证、业务后端和权益台账。商店负责产生可验证交易事实;客户端负责采集并传输凭证;后端负责验证、幂等、账号映射和退款处理;台账负责表达当前可用权益。任何一层都不应把另一层的状态未经验证直接继承。Android和iOS可以共享状态机设计,但不能混用凭证格式、订阅事件和测试环境。

工程落地

建议先建立原始Release基线,再生成基础保护候选和目标保护候选,最后验证商店签名包。每一组保存版本、构建链、商品配置、候选包Hash、后端验证状态、权益结果和回滚对象。CI/CD将构建、安装、购买、验单、退款、重复提交和权益回收拆成独立门禁;任何未知状态都进入人工复核,不自动发放高价值权益。

攻防视角

攻击者可能修改本地购买回调、替换数据库、重放凭证、伪造账号映射或批量调用权益接口。客户端加固主要抬高补丁和仿冒成本,服务端验单负责阻止无效交易,账号与行为策略负责识别异常。防守方不能只依赖某个布尔值,也不能把异常设备信号直接等同欺诈;观察、挑战、限额、延迟和人工复核应形成分级动作。

风险边界

本文没有执行客户包、真实商店账号或生产交易测试。示例字段均为方法说明,不构成法律、财务或商店审核承诺。正式上线前应由客户确认商品、税务、退款、隐私、数据保存、客服和回滚责任。不得在公开页面放置完整purchaseToken、服务账号、私钥、客户订单或可运行的绕过代码。

主目标与非目标

本次文章主目标是建立可被研发、产品和安全团队采用的内购权益安全门禁模型。非目标包括:不声称御盾已通过所有Billing或StoreKit版本、不替代Google或Apple官方接入、不提供生产凭证、不把一份方法文档当作客户PoC结论。

目标项 需要验证的对象 状态表达
交易真实性 商店API与签名交易 已定义方法,客户项目待验证
幂等发放 重复Token和重试 已定义门禁,客户项目待验证
退款回收 退款、撤销和订阅替换 已定义状态,客户项目待验证
客户端保护 关键调用链和运行时逻辑 需候选包PoC

事实依据与脱敏证据补充

公开依据只用于说明协议和责任边界,不等同于御盾已对每一种Billing、StoreKit、设备、商店或渠道组合完成实测。客户项目应在自己的测试商品、账号、地区和生产配置下复核。若官方API返回暂时不可用,系统应保留待处理状态,而不是向用户展示永久有效权益。若同一凭证重复到达,台账应返回原处理结果,不能新增一条发放记录。

购买状态与回滚说明

内购发布门禁还应覆盖账号切换、设备重装、覆盖升级、弱网重试、服务端超时、退款通知延迟、订阅升级和降级。每种场景都要记录客户端显示、后端状态、权益台账和最终接口结果。失败时保留可回滚版本,避免为了快速恢复而直接放开本地发放逻辑。对高价值权益,可先进入待确认状态,再根据后端核验和风险信号转为可用。

交付与转化边界

御盾PoC的交付物可以包括候选包身份表、调用链保护范围、商店验单字段表、权益状态机、异常处置矩阵和未覆盖清单。交付物不包含生产密钥、完整交易凭证、客户订单或可复用的绕过脚本。需要进一步评估时,请通过产品页提交Android或iOS Release候选包、一条付费业务路径和测试环境说明。

测评目标与非目标

本次安全评估的主目标是确认交易凭证、服务端验单、权益台账和客户端保护之间的责任边界。非目标是不对客户项目作绝对防盗刷承诺、不替代商店审核、不公开生产凭证、不把单一设备或完整性信号直接等同欺诈。

测评目标 观察对象 通过口径
正常购买链路 客户端、商店、后端和台账 只发放一次正确权益
重复凭证处理 同一Token重试与并发 幂等返回,不重复发放
退款与撤销 状态通知、台账和接口 权益按规则回收或冻结
客户端修改 本地状态和调用链 后端仍要求有效交易和账号映射

来源报告事实映射

报告事实 公开支持 对本文的作用
客户端购买状态不应直接发放权益 Play Billing安全文档 说明责任分层
Token需要服务端验证 Developer API文档 说明验单步骤
订阅和退款会改变资格 App Store Server API文档 说明台账同步
iOS交易包含签名信息 StoreKit文档 说明JWS边界
完整性信号需要业务解释 Play Integrity文档 说明分级处置

字段动态时间线

购买事件从客户端发起、商店确认、凭证提交、后端核验、台账写入到权益可用,状态会随重试、退款、撤销和订阅替换变化。每个时间点应保存脱敏Token引用、应用版本、账号映射、验证时间和处理版本;不保存完整凭证到公开日志。

相关阅读