先看结论与判断条件

  • requestHash 必须基于确定性序列化的业务请求计算,任何序列化差异都会导致绑定失效。
  • 工程判断:若项目采用服务端 nonce,可将它与当前会话和操作记录关联;具体生成、过期和消费规则需由后端威胁模型确定,现有证据不能替项目作结论。
  • 工程判断:请求核对可同时记录 requestHash 结果、nonce 状态和接收时间;是否需要最新性或过期检查,取决于项目实际采用的绑定方案。
  • 工程判断:完整性信号缺失时可按操作风险选择拒绝、重试或增强认证;这是后端策略建议,并非现有来源证明的固定产品行为。
  • 设备端 instrumented test 可验证加固后完整性 API 调用与 requestHash 逻辑正常。
  • 安全发布过程需保留构建、验证证据,确保变更可追溯,避免绑定逻辑被意外破坏。
  • 工程判断:若加固前后 requestHash 不同,应直接比较输入字节和字段来源;没有差异证据时,不应推定编译顺序或资源处理影响了计算。
  • 所有证据仅用于回归绑定是否失效,不能将完整性信号等同于设备绝对可信或攻击已被阻断。

加固后 Play Integrity 请求绑定的验证范围与前提

请求绑定只看一件事:令牌里的 requestHash,是否对应后端准备执行的那笔业务请求。客户端先把约定字段变成稳定字节并计算摘要;后端解密令牌后,用自己保存的请求快照重算。加固前后若出现差异,先保留两侧原始字节,再判断是字段集合、序列化还是传参发生了变化。

验证范围限定在“请求绑定”这一环节,不扩展到设备完整性评判本身。Play Integrity 返回的设备与软件信号只作为后端决策输入,不能据此证明设备绝对可信。准备测试时,保留客户端请求的稳定序列化格式、后端保存的字段副本,以及双方采用的哈希算法和输入契约。

绑定验证的前提还包括:服务端已经正确实现完整性令牌解密与验证流程;客户端在调用完整性 API 时传入了正确的 nonce;业务请求的关键字段(如操作类型、目标资源 ID、金额、时间戳)已被纳入 requestHash 计算。这些前提若缺失会导致误判,需核对具体日志字段确认。因此,验证工作必须结合服务端日志与客户端请求的完整上下文,排除因配置错误导致的假性失效。

应用版本、加固配置或服务端序列化代码有变化时,就重新跑同一组绑定样本。测试记录应能指出哪一侧的字节先发生变化。结果不一致时先暂停这份候选包,修正后再复测;发布检查点只需要依据这项明确结果,不必再附加抽象的合规口号。

请求绑定验证的四大前提
前提项要求不满足时的现象检查方法
requestHash 计算一致性客户端与服务端使用同一序列化算法、相同字段集合和同一哈希函数哈希不一致,服务端拒绝请求提取客户端请求原始字段,在服务端重算并比对
nonce 与操作绑定应为每次操作生成 nonce 以防止重放,服务端记录 nonce 与用户、操作 ID 的映射重放攻击可能成功,或绑定失效无法检测查询服务端会话存储中 nonce 的最新绑定记录
完整性令牌验证正确服务端成功解密令牌并验证签名,获取原始请求详情无法核实 requestHash,所有绑定结果不可信调用 Google Play 服务验证接口,确保返回码正常
加固未扭曲请求构造加固后的代码路径仍调用相同的请求打包逻辑,无额外字段插入或排序变化requestHash 意外变化,与旧版本不兼容对比加固前后同一业务请求的序列化输出
  • 确认服务端已正确实现完整性令牌解密与验证流程
  • 确认客户端在调用完整性 API 时传入了正确的 nonce
  • 确认业务请求的关键字段已被纳入 requestHash 计算
  • 确认验证步骤已纳入自动化测试与发布流程

requestHash 生成与双侧核对的工程要点

requestHash 是将完整性请求绑定到具体业务操作的核心机制。根据官方文档,标准请求可利用 requestHash 把完整性响应固定到稳定序列化后的业务请求上。客户端必须在发起完整性检查前,将业务请求的若干字段按照确定的顺序排列,并生成摘要。字段可以包括操作类型、目标资源、数量、请求时间戳以及服务端下发的 nonce。任何字段的遗漏或顺序差异,都会使服务端重算得到的哈希与原值不同,进而导致绑定验证失败。

JSON 对象内容相同,输出字节也可能不同,例如属性顺序、转义方式和数字格式不一致。双方需要约定一种可重复的规范化方法;RFC 8785 的 JCS 是可选方案之一,它把这些细节固定下来。若项目使用自定义规则,也要用跨语言样本证明客户端与后端得到相同字节,不能依赖各语言默认行为。

服务端重算时不应依赖客户端传来的业务字段原始值,而应从自身保存的请求记录中取出相同的业务上下文。例如,服务端在下发 nonce 时可以将该 nonce 与用户意图操作的参数快照一同保存,然后在验证阶段重新取出这些参数进行哈希计算。这样能避免恶意客户端篡改业务字段后仍提供相匹配的 requestHash。若服务端仅比对客户端自带的 requestHash 而不再自行计算,则绑定形同虚设。

验证时可以使用自动化脚本离线检查一组请求记录。代码应当从服务端存储中读取原始业务请求和客户端上传的 requestHash,重新调用与客户端相同的规范化与哈希函数,对二者进行比对。若哈希相同,则绑定未断裂;若不相同,则必须排查序列化算法或字段集合是否发生了变更。这种脚本可纳入 CI 流水线,每次构建后对历史请求抽样验证,若发现哈希失配则立即中断发布流水线。

  • 业务请求的序列化格式是否有明确文档且双方代码遵循同一规范
  • 哈希输入是否包含所有关键业务字段与 nonce
  • 客户端计算 requestHash 的代码是否在加固后仍然输出相同的字节序列
  • 服务端重算时所用字段是否来源于自己存储的请求快照而非客户端提交的副本
  • 是否定期使用离线工具核对生产日志中的 requestHash
加固后验证 Play Integrity 请求绑定未失效:requestHash 核对与服务端会话控制
#!/usr/bin/env python3
import sys
import json
import hashlib
from pathlib import Path

def canonical_json(obj):
    """Generate RFC 8785 JCS canonical JSON string."""
    return json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False)

def compute_sha256_hex(data_str):
    """Compute SHA-256 hex digest of a UTF-8 encoded string."""
    return hashlib.sha256(data_str.encode('utf-8')).hexdigest()

def main():
    if len(sys.argv) != 2:
        print("Usage: verify_binding.py <integrity_check_result.json>", file=sys.stderr)
        raise SystemExit(2)

    input_path = Path(sys.argv[1])
    if not input_path.is_file():
        print(f"Error: File '{input_path}' not found.", file=sys.stderr)
        raise SystemExit(2)

    try:
        content = input_path.read_text(encoding='utf-8')
        data = json.loads(content)
    except (json.JSONDecodeError, OSError) as e:
        print(f"Failed to parse input file: {e}", file=sys.stderr)
        raise SystemExit(2)

    client_request_hash = data.get("requestHash")
    business_request_payload = data.get("businessRequest")
    expected_nonce = data.get("nonceBound")
    actual_nonce = data.get("nonce")

    if not isinstance(client_request_hash, str) or not client_request_hash:
        print("Validation failed: Missing or invalid 'requestHash' in input.", file=sys.stderr)
        raise SystemExit(2)

    if not isinstance(business_request_payload, dict) or not business_request_payload:
        print("Validation failed: Missing or invalid 'businessRequest' object in input.", file=sys.stderr)
        raise SystemExit(2)

    canonical_body = canonical_json(business_request_payload)
    computed_hash = compute_sha256_hex(canonical_body)

    if client_request_hash != computed_hash:
        print("Validation failed: requestHash mismatch indicates binding breakage.", file=sys.stderr)
        raise SystemExit(2)

    if expected_nonce is not None and actual_nonce != expected_nonce:
        print("Validation failed: Nonce mismatch detected, possible replay attack.", file=sys.stderr)
        raise SystemExit(2)

    print("Request binding verification successful: hash and nonce match.")

if __name__ == "__main__":
    main()

服务端 nonce 会话与时效性控制

nonce 是服务端为每次操作生成的随机值,客户端在调用 Play Integrity API 时需将其传入。Integrity 响应中会原样返回该 nonce,从而将 API 调用绑定到特定的服务端会话。加固本身不会干扰 nonce 的生成,但加固后若客户端传递了错误的 nonce(例如由于内存混淆导致字符串变量被截断),服务端将核对失败。验证绑定失效是否由 nonce 引起,需要检查服务端收到的完整性令牌内的 nonce 是否与之前下发给该会话的值完全相同。

服务端应为每次操作生成 nonce 以防止重放。一般做法是 nonce 在生成后若干分钟内有效,到期后即使用户请求未到达,服务端也拒绝接受过期 nonce 对应的令牌。此举用于防止截获的完整性令牌被延时重放。验证流程中,服务端需先校验 nonce 的生成时间与当前时间之差,只有在窗口内的 nonce 才继续后续 requestHash 核对;否则直接丢弃请求。若加固导致客户端请求延迟(如启动时额外校验使得 API 调用后延),可能触发超时,表现为正常绑定失效。

每一条 nonce 应仅允许使用一次。服务端在成功验证一条完整性令牌后,应立即将对应 nonce 标记为已使用或从存储中删除,避免同一令牌被复用。若加固版本的应用在某些条件下无意间重复发送同一 nonce(例如网络失败自动重试时未启用新的 nonce),则第一次请求成功,第二次会被拒绝,造成业务中断。检查绑定元数据时,若发现同一个 nonce 被验证两次以上,即可判定存在重放或客户端逻辑错误。

会话绑定方面,nonce 还应与服务端的用户会话标识进行关联。当用户重新登录或会话过期时,旧的 nonce 应一并失效。加固后的应用可能改变会话管理机制,例如将令牌存储在更安全的区域,但在切换过程中可能丢失 nonce 的关联性。验证时可通过模拟用户登出再登入后,尝试使用旧 nonce 发起完整性请求,服务端必须返回失败。如果允许旧 nonce 通过,则表明会话绑定已失效,是严重的安全退化。

nonce 验证失败原因排查表
失败类型可能原因验证方法加固关联度
nonce 不匹配客户端传入的 nonce 与服务端下发的不一致对比请求日志中客户端发出的 nonce 与服务端下发的 nonce,检查是否被混淆或截断中,若加固修改了字符串处理可能引入
nonce 过期客户端请求到达时间超过 nonce 有效期检查客户端时间戳、网络延迟和加固引入的额外延迟低,但可能因加固后启动变慢而增加延迟
nonce 重复使用重放攻击或客户端库错误导致同一 nonce 多次发送服务端记录 nonce 消费日志,检查是否出现重复使用记录低,主要是客户端逻辑问题
nonce 未绑定会话服务端未将 nonce 与用户会话关联,或会话已失效但 nonce 仍可用登出后使用旧 nonce 请求,观察是否被拒绝无,属于服务端设计缺陷
  • 检查服务端收到的完整性令牌内的 nonce 是否与之前下发给该会话的值完全相同
  • 校验 nonce 的生成时间与当前时间之差是否在有效窗口内
  • 记录 nonce 的生成来源、会话关联与有效时间;一次性消费检查见下一节
  • 确认 nonce 与服务端的用户会话标识进行关联

重放防护与请求顺序检查

仅靠 requestHash 与 nonce 无法防御跨令牌重放等特定攻击面。攻击者可能在拦截合法请求后,使用同一个 requestHash 和 nonce 在短时间内向服务端重放。虽然 nonce 一次性使用可阻止完全一样的令牌被接受,但若攻击者在 nonce 有效窗口内,使用不同的完整性令牌(例如从其他设备获取)但携带相同的业务请求摘要,仍可能绕过约束。因此必须增加请求级别的顺序或上下文检查。

计数器或序列号方案是服务端为每个用户或设备维护一个递增的操作计数器或时间序列,将计数器值纳入 requestHash 的计算。这样即使业务参数完全相同的两次操作,因为计数器不同,requestHash 也会不同。验证时服务端从存储中取出预期计数值,与请求中的数值比较,确保不落后或重复。加固后的应用可能改变计数器的持久化方式,如果造成计数器回退或混乱,将导致合法的连续操作也被判定为重放,从而阻断正常业务流程。

重放检查还应与完整性令牌本身的时间戳配合。Play Integrity 响应中包含请求的时间信息,服务端可以将其与业务操作发生的时间窗口进行比对。如果时间差异过大,即使 requestHash 正确也可能判定为异常。加固若导致应用内部时钟偏移(例如某些保护措施修改了系统时间获取 API),可能造成时间戳失真,验证绑定时需排除该因素。

排查重放时应对比具体日志字段或指标。绑定失效排查时,应检查是否存在同一个 requestHash 在短时间内被多个不同完整性令牌携带的情况,这暗示着绑定已被剥离或存在重放工具。若这类现象在加固后出现,需对比加固前后请求流,确认是否加固导致令牌生成逻辑被复用或缓存。服务端保留完整的请求日志与顺序元数据是验证的基础。

  • 服务端是否实施了 nonce 一次性消费机制
  • 业务请求中是否包含了操作序号或时间戳等不可预测的动态值
  • 完整性令牌原始时间戳是否与业务服务器时间偏差在可接受范围内
  • 是否监控到同一 requestHash 被不同令牌使用的异常模式
  • 加固后计数器持久化是否正确,无回退现象

完整性决策的降级路径设计

Play Integrity 超时、无响应或返回低置信信号时,处理方式应由后端按业务风险决定。公开内容读取可以重试或有限放行,账户修改和资金操作则可选择拒绝或增加认证。这里验证的是异常分支有没有绕过请求绑定,而不是把某一种降级策略包装成所有项目都适用的答案。

降级决策应根据业务风险等级制定。例如,对于读取公开内容,可允许无完整性信号;对于修改账户资料,可要求至少基础的设备完整性;对于转账等操作,必须要求强完整性信号且 requestHash 验证通过。加固本身不改变这些决策权限,但若加固后的应用在 API 不可用时直接跳过完整性检查并继续操作,则降级路径过宽,绑定保护完全失效。

验证降级路径是否合理的一个有效方法是触发人工故障:在测试环境中故意让 Play Integrity API 返回无效令牌或超时,观察服务端是否仍然执行了受保护操作。如果操作被执行,则降级逻辑存在严重缺陷。同样,若服务端返回通用错误而非明确拒绝,攻击者可能通过制造故障来迫使应用降级。加固后的应用应确保在 API 异常时,将决策权完整地交给服务端,自身不做任何允许判断。

降级分支改动后,保存修改前后的策略、测试输入和后端返回值。让测试环境分别制造超时、空令牌和 requestHash 不匹配,观察高风险操作是否仍被执行。这组记录能说明异常路径的实际行为,也便于下一次版本变更时复用。

基于不同完整性结果的降级决策示例
场景完整性令牌状态requestHash 验证允许/拒绝
查看公开文章令牌缺失或验证失败不适用允许(低风险)
更新个人头像令牌验证失败不适用拒绝,要求重新登录或稍后重试
小额支付令牌有效但设备完整性返回空响应requestHash 匹配根据风险策略可要求增强认证后允许
大额转账令牌有效且设备完整性通过requestHash 匹配允许,但需额外风控检查
大额转账令牌有效但设备完整性失败requestHash 匹配拒绝,不允许降级
任何受保护操作令牌缺失无法验证拒绝
  • 确认降级路径不能无原则地放行操作
  • 确认若无法获得可验证的完整性令牌,后端应拒绝执行高风险操作
  • 确认降级决策根据业务风险等级制定
  • 确认加固后的应用在 API 不可用时不会直接跳过完整性检查

利用自动化测试固化绑定验证

依赖人工每次检查绑定是否失效既不现实也不可靠。应将验证逻辑纳入自动化测试,特别是运行在真实 Android 设备上的 instrumented test。Android 测试框架允许直接调用应用内的业务请求构建代码和完整性 API 封装层,从而在加固后的包中端到端验证 requestHash 的生成与传参过程。测试需要模拟完整的流程:应用发起业务请求、计算 requestHash、调用完整性 API 并获取令牌、将令牌和业务数据发送给测试专用的服务端端点,最后由测试断言服务端返回成功。

instrumented test 可以覆盖不同业务场景和多款设备,但根据文档,单一设备通过不代表完整 API、ABI 和厂商矩阵。因此测试应选取代表性子集,包括不同系统版本和厂商,以暴露可能因加固或设备差异导致的序列化不一致。测试失败时,必须输出具体差异(例如客户端生成的 requestHash 与服务端预期值),便于开发人员定位是序列化规则变更还是字段缺失。

本节代码块给出一个离线核对器:读取已经规范化的请求体,重算 SHA-256,再与预期 requestHash 比较。它只读输入文件,格式错误或摘要不一致时返回非零状态,适合放进构建后的检查步骤。它没有连接 Play Integrity 服务,也不验证设备信号。

自动化测试还应模拟重放场景与 nonce 过期场景,确认服务端按预期拒绝。这些测试可在每次发布前运行,连同其他安全测试组成回归套件。若加固导致绑定逻辑变动,测试会率先捕获,防止流入生产。通过这种方式,团队可以确保持续监控绑定有效性,并及时发现潜在问题。

  • 应将验证逻辑纳入自动化测试,特别是运行在真实 Android 设备上的 instrumented test
  • 测试应选取代表性子集,包括不同系统版本和厂商
  • 测试失败时,必须输出具体差异
  • 自动化测试还应模拟重放场景与 nonce 过期场景
Kotlin 命令行校验器:对规范化请求体重算 requestHash
import java.nio.file.Files
import java.nio.file.Path
import java.security.MessageDigest
import kotlin.system.exitProcess

private fun hex(bytes: ByteArray): String =
    bytes.joinToString("") { byte -> "%02x".format(byte) }

fun main(args: Array<String>) {
    if (args.size != 2) {
        System.err.println("usage: verify-request-hash <canonical-request-file> <expected-sha256>")
        exitProcess(2)
    }

    val requestPath = Path.of(args[0])
    if (!Files.isRegularFile(requestPath)) {
        System.err.println("request body file is missing")
        exitProcess(2)
    }

    val expected = args[1].lowercase()
    if (!Regex("[0-9a-f]{64}").matches(expected)) {
        System.err.println("expected requestHash must be 64 hexadecimal characters")
        exitProcess(2)
    }

    val requestBytes = Files.readAllBytes(requestPath)
    val actual = hex(MessageDigest.getInstance("SHA-256").digest(requestBytes))
    if (!MessageDigest.isEqual(actual.toByteArray(), expected.toByteArray())) {
        System.err.println("requestHash does not match the canonical request body")
        exitProcess(1)
    }

    println("requestHash matches the canonical request body")
}

构建证据与发布前安全检查(基于 SSDF)

为了能回放一次绑定差异,至少记录候选包摘要、加固配置版本、完整性调用库版本和测试样本编号。NIST 的安全开发框架支持保留这类发布证据,但它不规定御盾或任何具体工具应怎样实现 requestHash。

构建完成后,对候选包运行设备端绑定测试和离线字节核对,并把结果关联到同一产物摘要。任一测试不一致就暂停该包;测试工具、报告存储位置和保留周期由团队按现有发布系统确定。

此外,需对涉及 requestHash 计算的代码模块和序列化逻辑进行代码审查,确保其不会因加固后字节码变换而发生非预期的行为。审查结果应同构建证据一并归档。若后续出现绑定失效报告,可依据这些证据快速定位是否由加固或业务逻辑变更导致。这有助于在出现问题时迅速找到根源,减少停机时间。

将安全证据留存纳入发布流程便于快速定位根因。例如,可定期抽取线上请求,验证 requestHash 匹配率是否下降,若突然下降则可关联到最近的发布版本和加固变更。这种持续监控机制能够及时发现并解决潜在的安全隐患,确保系统的长期稳定运行。

  • 是否记录了加固工具的版本和配置
  • 是否在发布前自动运行绑定验证测试并归档结果
  • 是否对请求序列化代码进行了变更审查
  • 是否建立了线上 requestHash 吻合率监控
  • 是否有流程在测试失败时阻止发布

绑定失效的常见原因与排查步骤

当确认请求绑定在加固后失效,常见的浅层原因包括:客户端计算 requestHash 时使用的字段序列发生了意外变化、nonce 在传输中丢失或被混淆、服务端重算时采用了不同的 JSON 规范化库或参数、以及加固工具优化了字符串常量导致序列化输出不符预期。这些问题通常能通过对比加固前后客户端发出的原始业务请求字节序列来发现。

排查应从最基础的一致性开始:在不涉及完整性 API 的情况下,让日志同时输出客户端发送给服务端的业务请求明文与服务端收到的请求明文,确认二者一致。如果明文已有差异,则问题很可能出在加固引入的序列化代码改变或网络传输层编码。若明文一致,再检查完整性令牌中的原始请求字段是否与明文吻合。Play Integrity 响应中的请求详情报文可由服务端解密后导出,用于比对。

另一种隐蔽原因与 nonce 时效有关。加固导致应用启动时间延长,使得用户操作时间与 nonce 生成时间不处于同一有效时间窗口。当服务端设置的窗口较紧时,正常操作的 nonce 被判定过期。此时应调整 nonce 的有效期或优化启动性能,直接给出调整建议。

若上述步骤均未发现异常,应考察加固是否改变了某些依赖系统属性的代码,例如通过反射获取的字段在加固后被重定向。可以通过 instrumented test 在现场捕获计算 requestHash 前的完整数据结构,并与未加固版本对比。任何微小的字符串编码或数字精度变化都可能导致摘要不一致。找到原因后,要么调整加固规则使该部分免于修改,要么重新约定双方可接受的规范化规则。排查时不必先怀疑加固工具本身存在安全漏洞。

  • 对比加固前后客户端发送的业务请求字节是否一致
  • 检查服务端解密出的完整性令牌中的原始请求字段
  • 确认 nonce 生成与接收的时间差是否在服务端窗口内
  • 通过 instrumented test 捕获加固前后序列化输出,并对长度、编码和首个差异位置做逐字节诊断
  • 通过 instrumented test 现场捕获计算前的完整数据结构
  • 审查加固配置,将关键序列化代码加入不优化列表

事实依据与适用边界

以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。

本文判断事实或工程依据适用限制
标准请求可用 requestHash 把完整性响应绑定到稳定序列化后的业务请求,并由服务端重算核对。Play Integrity standard requests完整性信号不是绝对可信根,最终允许或拒绝仍由服务端结合业务上下文决定。
服务端必须基于自身保存的请求快照重新计算 requestHash 进行核对,不能仅依赖客户端提供的摘要。Play Integrity standard requests该机制只能证明请求与业务操作的绑定关系,不能证明设备可信、未遭 Root 或攻击已被阻断。
完整性信号应靠近受保护操作请求生成,并由后端解密、验证和决策。Play Integrity overview平台信号不证明 VMP 配置、Root 状态或攻击已被阻断。
完整性响应包含请求详情和多类平台信号,后端应按业务场景组合使用。Play Integrity verdicts任何单一 verdict 都不是绝对信任、Root 判定或 VMP 配置证明。
后端在验证时需综合考虑设备完整性、应用完整性和其他信号,不能只依赖 requestHash。Play Integrity verdicts组合后的决策仍不能保证设备绝对安全,仅作为风险控制的参考因素。
签名或摘要输入若使用 JSON,需要先按 RFC 8785 规范化以保证跨环境字节一致。RFC 8785 JSON Canonicalization SchemeJCS 只解决确定性表示,不提供认证、重放控制、完整性 verdict 或业务授权。
依赖真实 Android 运行时和系统 API 的语义应通过设备端 instrumented test 验证。Android instrumented tests单一设备通过不能代表完整 API、ABI 和厂商矩阵,需配合多设备测试。
安全发布应保留来源、构建、验证和变更证据,并把供应链风险纳入开发流程。NIST SP 800-218 SSDFSSDF 是组织级实践框架,不定义某个 App 加固产品的具体功能,具体实现需组织自行设计。

工程常见问题

加固后 requestHash 一直不匹配,先看哪里?

先留住参与计算的原始字节,不要只看解析后的 JSON。把加固前、加固后和后端重算结果放在一起比较,找出第一个不同位置,再回查字段顺序、字符编码或数字格式。只有字节差异指向某段代码时,才调整对应保护规则。

客户端用固定 nonce,会不会更容易做回归?

固定值可以放在完全隔离的序列化单元测试里,但不适合真实绑定流程。真实请求需要能区分不同操作,nonce 的生成、会话关联与消费方式由后端方案决定,测试应复现这套实际规则。

用户退出登录后,旧 nonce 怎么验证?

准备一条登录期间下发的 nonce,退出后再提交对应令牌,记录后端返回和消费状态。若项目设计规定 nonce 随会话失效,这条请求就应落在明确的拒绝分支;结论以服务端策略和日志为准。

Play Integrity 暂时不可用时,绑定测试看什么?

重点看受保护操作有没有绕过后端决策。测试环境可制造超时、空令牌和解析失败,分别记录公开读取、账户修改等不同风险操作的结果,再与项目预先定义的降级表核对。

自动化绑定测试应该放在哪一步?

放在加固产物生成之后最有价值,因为此时测到的是实际候选包。每次序列化代码、完整性库或后端重算逻辑变化时也应重跑,并把结果绑定到同一产物摘要。

加固后怎样定位 requestHash 的序列化差异?

在计算摘要前捕获规范化字节,输出长度、编码和首个差异位置。先用固定样本做离线重算,再用设备端测试走真实请求构建路径;两者结果分开记录,便于判断差异来自业务代码还是运行环境。

新增字段参与 requestHash 后,怎样做回归?

先更新双方共同使用的字段契约和固定样本,再让客户端、后端各自计算摘要。测试既要包含正常值,也要包含缺字段、空值和边界数字;任何一侧仍沿用旧字段集合都会直接暴露出来。

离线核对通过,就能说明线上绑定安全吗?

离线结果只说明指定请求体能够重算出相同摘要。线上还涉及令牌解密、请求详情、会话状态和后端授权。把离线一致性作为一个检查点即可,不能把它扩展成设备可信或攻击已被阻断的证明。

想用自己的 App 验证?

提交候选包、目标系统和关键业务路径,申请御盾 PoC 与兼容性评估。

继续阅读: App 加固 PoC 如何形成发布结论