先看结论与判断条件

  • PoC 的最小单位是具有业务入口、状态变化和可判定结果的闭环路径,不是单个页面、按钮或测试人员的一串点击记录。
  • 优先级应由失败损失、被保护资产、状态不可逆性、触发频率、平台依赖和诊断难度共同决定,收入不是唯一排序标准。
  • 启动、登录、授权、支付前后状态和高风险 SDK 要覆盖允许、拒绝、取消、重试、恢复与进程重建,不能只跑最顺畅的正向分支。
  • 同一代码仓库的 build type、product flavor、资源、applicationId、签名配置和 SDK 组合可能不同,PoC 结论必须绑定精确候选。
  • 依赖 Android 运行时、组件和系统 API 的语义应在设备端验证;云矩阵用于扩展覆盖,但不能替代真实渠道和项目状态。
  • PoC 通过只能支持已执行候选、路径、状态和环境内的结论,不能外推全部功能、全部设备、线上性能或防护强度。

先把 PoC 定义成最小业务闭环,而不是页面巡检

加固 PoC 的直接目标,是确认精确候选经过保护处理后,关键业务仍能在受控条件下完成,并且失败时能够定位和回退。一个合格路径至少包含入口、前置状态、用户动作、系统依赖、状态变化、成功标准和失败标准。只有能回答这些字段,测试结果才可复核;只写“首页正常”“支付可用”或“功能无异常”,无法说明执行了什么。

页面数量不能代表风险覆盖。登录页可能展示正常,但令牌刷新在进程重建后失败;支付页可能顺利拉起,取消支付后订单却停留在处理中;授权页面可能允许合法用户进入,却没有验证失效订阅和越权账号。PoC 应围绕资产和状态机组织,而不是从菜单第一项机械点到最后一项。

工程上可以把 pathId 作为证据主键,并绑定候选 SHA-256、variantId、设备环境、测试数据类别和执行回执。基线包与加固候选必须使用同一前置状态和判定口径。若基线已经失败,应先处理基线或记录既有缺陷,不能把它算作加固回归,也不能删除失败路径后宣称整体通过。

一条可验收 PoC 路径的固定字段
字段要回答的问题合格证据常见误判
入口从哪里进入业务Activity、深链或受控入口只写页面名称
前置状态账号、订单和权限处于什么状态可重复夹具与状态摘要依赖人工临时准备
状态变化执行前后什么被改变before 与 after 契约只看界面提示
成功标准什么结果算完成业务断言与设备回执以没有闪退代替
失败标准什么现象立即阻断明确错误码、状态或超时条件测试后口头解释
回退失败后怎样恢复环境幂等清理或受控重置继续复用污染状态

用业务损失和技术暴露面决定先后顺序

第一层排序看业务损失。会阻止用户启动应用、登录账号、获得已购权益、完成付款或恢复交易的路径,通常应进入首轮。这里的“损失”要写成具体后果,例如用户无法到达服务、合法授权被拒、重复提交、订单状态悬空或本地记录与服务端不一致,而不是笼统写成“影响体验”。

第二层排序看被保护资产与技术暴露面。包含本地授权判断、Native 库加载、动态模块、WebView 桥、第三方支付 SDK、账号 SDK、推送入口或深链解析的路径,更容易受到类加载、反射、资源、签名、组件和进程边界变化影响。OWASP MASVS-RESILIENCE 把抗篡改与抗逆向放在纵深防御语境中;因此 PoC 既要检查业务连续性,也不能把一次功能通过写成防护强度证明。

第三层排序看可诊断性与不可逆性。一次失败如果会写入订单、消耗兑换码、改变订阅、覆盖存档或使测试账号进入不可恢复状态,就必须先设计隔离数据、幂等键、查询接口和回退步骤。高频但易恢复的路径与低频却不可逆的路径不能只按访问量比较,应由产品、研发、测试和安全共同确认优先级。

核心路径优先级的工程评分维度
维度高优先信号需要的项目证据不能直接推断
收入付款、续费、恢复购买或权益到账订单状态机和测试环境真实支付金额与成功率
授权本地或离线决定高价值能力允许与拒绝账号夹具服务端授权已安全
可达性启动、首屏或初始化链阻断全应用冷启动回执与退出信息线上启动分位数
账号登录、刷新、退出或换账号改变会话令牌状态契约生产身份系统无风险
数据完整性操作可能重复、丢失或悬空前后状态与幂等键所有异常均已恢复
高风险依赖SDK、Native、WebView 或动态模块参与精确版本与调用边界依赖不存在未知缺陷

启动与登录要覆盖进程重建和会话失效

启动路径不是看到首屏就结束。应区分冷启动、温启动、后台返回、进程被系统回收后的恢复以及从通知或深链进入。Android app startup time 区分 TTID 与 TTFD:初始画面出现和应用真正可交互、数据完整展示不是同一时刻。PoC 要保存测量条件和关键事件,但单次耗时只用于回归诊断,不能写成线上分位数或加固导致的因果结论。

登录路径至少要有未登录成功、密码或验证码拒绝、已有会话刷新、令牌失效、主动退出和账号切换。加固可能触及 Application 初始化、序列化、反射、网络组件、Keystore 引用或 Native 依赖;因此测试既要观察界面,也要断言账号身份、令牌生命周期、本地数据隔离和服务端状态没有串用。

Android instrumented tests 在 Android 设备环境中运行,可访问组件和平台 API,适合验证依赖真实运行时的启动与登录语义。设备测试仍需使用受控测试账号和非生产密钥,日志应删除令牌、手机号和用户数据。单台设备通过只证明该候选在该环境下执行了指定断言,不能代表所有 API、ABI、厂商系统或账号策略。

启动与会话路径的状态闭环
路径前置状态关键断言阻断示例
冷启动进程不存在、缓存状态固定入口可达且数据完成展示只出现启动画面或初始化退出
进程重建已登录后回收进程身份与导航按契约恢复恢复到错误账号或空白页
登录允许有效测试凭据会话建立且业务身份一致界面成功但令牌未保存
登录拒绝无效或过期凭据拒绝且不污染旧会话错误账号继承旧权益
令牌刷新访问令牌失效、刷新条件有效刷新一次且请求可恢复循环刷新或重复提交
退出换号账号甲已登录数据清理后进入账号乙本地缓存跨账号泄漏

授权和支付必须验证前后状态,而非只看拉起成功

授权路径要同时验证合法允许和明确拒绝。测试夹具可覆盖有效授权、过期授权、未购买、离线宽限、设备变更和服务端撤销,但是否存在这些状态取决于项目规则。断言应落在实际能力是否开放、服务端记录是否一致和本地缓存是否按策略更新,不能只依据按钮颜色、弹窗文案或本地布尔值。

支付路径至少拆成创建订单、拉起渠道、用户取消、渠道失败、回调确认、权益到账和恢复查询。PoC 不必执行真实生产交易,可以使用官方沙箱或项目受控测试环境;但每一步都要保存 orderFixture、beforeState、afterState 和 failureCriterion。若没有可回滚环境,应先把路径标为阻塞,而不是改成只测支付页面能否打开。

状态转换比最终页面更重要。用户取消后订单应进入项目定义的可恢复状态,重复回调不得重复发放权益,进程在支付返回前被回收后仍要能够查询最终结果。涉及收入或授权的用例必须包含至少一个拒绝或中断分支,防止正向分支成功掩盖状态机错误。任何项目尚未提供的状态规则,都应保留为待确认事实。

支付与授权的前后状态设计
场景before事件after 与失败判据
合法授权授权有效且身份匹配进入受保护能力能力开放;误拒绝即失败
过期授权授权超过项目有效期尝试进入能力按规则拒绝;继续开放即失败
支付取消订单已创建未支付用户取消渠道流程状态可查询可恢复;权益到账即失败
支付成功订单待确认沙箱返回并完成确认订单与权益一致;只改本地显示即失败
重复回调订单已经确认重复提交同一回执结果幂等;重复发放即失败
进程中断外部支付页已打开应用进程被回收后返回主动查询收敛;永久悬空即失败

高风险 SDK 要按调用边界纳入路径,不做孤立冒烟

高风险依赖不是把所有第三方 SDK 各点一次,而是识别它们位于哪条核心路径、跨越哪些边界以及失败会留下什么状态。支付、账号、地图、推送、风控、广告、WebView 桥和 Native SDK 的初始化时机、组件声明、反射入口、资源、ABI 与回调线程不同。清单应记录精确版本、调用入口、结果回调和可替代降级路径。

若 SDK 仅在特定渠道、地区、product flavor 或动态模块中存在,代表样本必须真正包含该依赖。Android build variants 文档说明 build type、product flavor、source set、applicationId 和签名配置会组合成不同变体;所以“同一个仓库已经测过”不能证明另一变体具有相同资源、证书、SDK 或运行行为。

PoC 先选择能够代表最高业务损失和最高技术差异的候选,再把其他变体列为后续接受范围。具体样本方法可参考[多渠道多 Flavor App 的 PoC 样本选择](/zh-cn/articles/choose-representative-poc-variant/)。该内链处理变体选择,本篇只决定业务路径;两者都不能替代最终设备矩阵和发布渠道回执。

高风险依赖与业务路径的绑定字段
字段记录内容验证动作边界
dependencyId依赖名称与精确版本核对候选实际包含项不评价未测版本
variantIds出现该依赖的变体逐项绑定候选摘要同仓库不能代替
entryPoint初始化或调用入口设备端执行真实调用不包含秘密参数
callback成功、拒绝和异常结果断言线程与状态转换不把拉起当完成
fallback依赖不可用时的产品策略验证降级或明确阻断没有策略则待确认
dataClass路径处理的数据类别最小化测试数据和日志不保存真实用户数据

用路径清单生成正向、拒绝与恢复用例

手工表格最容易遗漏失败判据,下面的 Python 脚本读取 JSON 路径清单,校验 pathId 唯一、影响类别有效、前置条件与回退不为空,并把每个 scenario 展开为独立用例。输入中的 before、action、expected 和 failureCriterion 都必须存在;收入或授权路径还必须同时包含 allow 与 deny 场景,避免只有正向证明。

脚本只生成公开安全的用例骨架,不调用设备、不访问账号、不执行支付,也不包含攻击或绕过逻辑。生成结果可交给 instrumentation、云真机或现有测试框架执行。任何输入不完整都会以非零状态退出,使缺少状态契约的问题在设备资源被占用前暴露。

路径清单应由业务和研发共同填写,测试负责把可判定字段变成断言,安全人员检查加固影响面。候选摘要、签名、真实账号夹具和执行回执应在受控系统另行管理,不能塞进公开示例。脚本输出不是通过证据;只有同一候选的实际执行记录才能支持 PoC 结论。

  • 每个 pathId 在清单内唯一
  • 业务影响类别属于受控枚举
  • 入口、回退和代表变体不为空
  • 每个场景都有 before、action、expected 和 failureCriterion
  • 收入与授权路径同时包含 allow 和 deny
  • 输出只生成用例,不冒充设备执行回执
  • 候选、账号和签名材料留在受控系统
从业务路径清单生成 PoC 用例并拒绝不完整状态
#!/usr/bin/env python3
import json
import sys
from pathlib import Path

IMPACTS = {"revenue", "authorization", "availability", "account", "data-integrity", "high-risk-dependency"}
REQUIRED_SCENARIO_FIELDS = ("name", "kind", "before", "action", "expected", "failureCriterion")

def require_text(value, label):
    if not isinstance(value, str) or not value.strip():
        raise ValueError(f"missing non-empty {label}")
    return value.strip()

def load_inventory(path):
    data = json.loads(Path(path).read_text(encoding="utf-8"))
    if not isinstance(data, list) or not data:
        raise ValueError("inventory must be a non-empty array")
    return data

def build_cases(paths):
    seen = set()
    cases = []
    for path in paths:
        path_id = require_text(path.get("pathId"), "pathId")
        if path_id in seen:
            raise ValueError(f"duplicate pathId: {path_id}")
        seen.add(path_id)
        impact = require_text(path.get("businessImpact"), f"{path_id}.businessImpact")
        if impact not in IMPACTS:
            raise ValueError(f"unsupported impact for {path_id}")
        require_text(path.get("entry"), f"{path_id}.entry")
        require_text(path.get("rollback"), f"{path_id}.rollback")
        variants = path.get("variantIds")
        if not isinstance(variants, list) or not all(isinstance(v, str) and v.strip() for v in variants):
            raise ValueError(f"invalid variantIds for {path_id}")
        scenarios = path.get("scenarios")
        if not isinstance(scenarios, list) or len(scenarios) < 2:
            raise ValueError(f"at least two scenarios required for {path_id}")
        kinds = set()
        for index, scenario in enumerate(scenarios, start=1):
            normalized = {field: require_text(scenario.get(field), f"{path_id}.{field}") for field in REQUIRED_SCENARIO_FIELDS}
            kinds.add(normalized["kind"])
            cases.append({"caseId": f"{path_id}-{index:02d}", "pathId": path_id, "impact": impact, "variantIds": variants, **normalized})
        if impact in {"revenue", "authorization"} and not {"allow", "deny"}.issubset(kinds):
            raise ValueError(f"allow and deny scenarios required for {path_id}")
    return cases

if len(sys.argv) != 2:
    raise SystemExit(2)
try:
    result = build_cases(load_inventory(sys.argv[1]))
except (OSError, json.JSONDecodeError, ValueError) as error:
    print(str(error), file=sys.stderr)
    raise SystemExit(3)
print(json.dumps({"caseCount": len(result), "cases": result}, ensure_ascii=False, indent=2))

把路径放进设备矩阵,并保持候选与环境可追溯

业务路径选好后才进入环境覆盖。Firebase Test Lab Android matrices 将测试组合定义为设备型号、操作系统版本、方向和 locale 等维度,矩阵中一个执行失败会影响整体判断。设备选择应来自应用最低支持范围、真实设备分布和高风险差异,而不是挑一台方便的最新设备,也不能用大量重复设备制造覆盖感。

每次执行至少绑定 candidateDigest、variantId、pathId、caseId、设备型号、系统版本、ABI、locale、安装来源和测试数据版本。Android vitals 可以提供崩溃、ANR、启动和设备分布等线上质量信号,可用于发现值得加入 PoC 的高风险环境;其样本受到安装来源、用户同意和统计口径限制,不能代替候选级设备断言。

矩阵结果要按失败保守收敛。某个设备的登录恢复失败,不能被其他设备通过“平均掉”;应先判定是候选、环境、测试数据还是既有基线问题,再决定修复、缩小结论或记录项目限制。云设备也未必复现全部商店签名、厂商账号、推送网络和支付渠道,因此关键发布路径仍需要相应渠道的真实受控回执。

路径执行回执的追溯字段
对象固定标识为什么需要缺失后的处理
候选文件 SHA-256 与签名阶段防止替换文件后沿用结论阻断登记
变体variantId 与依赖摘要限定 flavor、资源和 SDK缩小结论
用例pathId 与 caseId对应业务状态和判据结果不可复核
设备型号、系统、ABI、locale解释运行环境差异不外推矩阵
数据fixtureVersion 与数据类别复现允许、拒绝和中断重新准备状态
回执时间、结果和原始证据摘要连接执行与结论保持未验证

用分层准入结论结束 PoC,不把局部通过扩大成产品承诺

PoC 结束时应逐路径给出 passed、failed、blocked 或 not-run,而不是只给一个总百分比。passed 表示精确候选在记录环境和状态下满足既定判据;failed 表示出现可复现偏差;blocked 表示缺少账号、沙箱、设备、签名或业务规则;not-run 表示不在本轮范围。阻塞项不能改名为通过,也不能从报告中删除。

结论还要分层:静态身份检查、设备安装与启动、核心路径状态契约、代表矩阵、渠道回执分别支持不同范围。OWASP 控制目录、平台文档和脚本校验提供方法依据,不证明某个项目已获得抗逆向强度、性能改善或攻击阻断。没有线上样本时,也不能根据 PoC 单次启动记录宣称生产分位数改善。

准备软件加固 PoC 时,可整理精确候选、代表变体、核心路径清单、允许与拒绝夹具、测试渠道、设备范围、失败判据和回退责任,再通过御盾中央平台提交申请。申请材料越接近可执行状态,双方越容易尽早发现缺失的沙箱、签名或业务规则;最终能力和适用方案仍以实际评估与项目回执为准。

PoC 准入层级与可支持结论
层级所需证据可以说明不能说明
候选身份摘要、variant 与签名阶段测试对象被锁定功能或保护已通过
安装启动设备安装、冷启动和退出记录指定环境可进入应用核心业务完整
路径契约允许、拒绝、中断与恢复断言指定路径状态正确未执行功能正确
代表矩阵选定设备组合逐项回执结论覆盖记录矩阵全部用户设备正确
渠道验证对应商店或分发链回执该渠道样本可交付其他渠道等价
生产观察合规线上指标与版本身份发现真实质量信号单次 PoC 的因果外推

事实依据与适用边界

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

本文判断事实或工程依据适用限制
依赖真实 Android 运行时、组件和系统 API 的语义可以通过设备端 instrumented test 验证。Android instrumented tests 说明 instrumented tests 在 Android 设备环境中运行并可访问平台 API。单台设备通过不能代表完整 API、ABI、系统版本、厂商与渠道矩阵。
Android 云测试矩阵可以按设备型号、OS、方向和 locale 等维度组织执行。Firebase Test Lab Android matrices 的入门资料说明矩阵配置和各执行结果。云矩阵仍需依据真实用户与最低支持范围选择,也不能自动复现全部账号和分发环境。
移动端抗篡改与抗逆向控制属于纵深防御的一部分。OWASP MASVS-RESILIENCE 列出与平台完整性、抗篡改和抗动态分析相关的韧性控制。控制目录不证明某个候选已经达到任何保护强度,也不替代服务端授权和发布链。
Android vitals 可提供崩溃、ANR、启动与设备分布等线上质量信号。Android vitals 文档说明其核心质量指标及可用于观察的应用质量信息。指标受 Play 安装来源、用户同意和统计口径限制,不能代替候选级 PoC 回执。
Android 启动分析应区分 TTID 与 TTFD,并记录启动状态和测量条件。Android app startup time 文档定义 TTID、TTFD 及启动性能分析语境。单次耗时不能代表线上分位数,也不能单独证明加固与性能变化存在因果关系。
build type、product flavor、source set、applicationId 与签名配置会共同形成不同构建变体。Android build variants 文档描述构建类型、产品风味、源集和变体配置。同一仓库不能证明不同 variant 具有相同 SDK、资源、证书和运行行为。
PoC 核心路径应按具体业务损失、状态不可逆性和技术暴露面排序,而不是按页面数量排序。工程判断:页面巡检不能覆盖令牌刷新、订单状态、授权拒绝、进程重建和依赖回调等跨页面状态。实际优先级必须由项目资产、业务规则、真实流量与失败成本确认。
收入与授权路径至少需要允许和拒绝分支,并保存前后状态与失败判据。工程判断:仅执行成功分支无法发现越权开放、失效授权沿用、重复发放和取消后状态悬空。具体状态名、沙箱能力、回退方案和接受条件取决于项目实现。

工程常见问题

加固 PoC 是不是把所有页面点一遍就够了?

不够。应选择具有业务入口、前置状态、状态变化和明确结果的核心闭环,重点覆盖启动、登录、授权、支付与高风险依赖的正向和失败分支。

核心路径只能按收入高低排序吗?

不能。还要考虑全局可达性、授权资产、数据不可逆性、技术依赖、触发频率和失败后的诊断成本,低频但不可恢复的路径也可能优先。

支付 PoC 只要能拉起渠道页面是否算通过?

不算。还要验证取消、失败、确认、重复回调、权益到账、进程中断和恢复查询,并使本地与服务端状态按项目契约收敛。

一个 flavor 通过能否代表同仓库的其他渠道包?

不能直接代表。不同变体可能组合不同 applicationId、资源、签名、SDK 和源集,结论必须绑定精确候选,其他变体需按差异另行接受。

云真机矩阵通过能否证明全部用户设备没有问题?

不能。矩阵只覆盖选定型号、系统、方向和 locale,且未必复现真实商店签名、厂商账号、网络和历史数据,结论应限制在已执行范围。

申请软件加固 PoC 前应准备哪些材料?

准备候选文件及摘要、代表变体、核心路径清单、允许与拒绝测试状态、沙箱渠道、设备范围、成功与失败判据、回退责任,再通过御盾中央平台提交申请。

想用自己的 App 验证?

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

继续阅读: 御盾 VMP 接入前准备清单