先看结论与判断条件
- 停止支持范围要精确到工具、插件、平台 SDK、组件和日期。
- 应用台账必须绑定当前候选与构建材料,不能用最新源码状态替代线上版本。
- 旧链基线保存可复现输入、输出摘要、签名边界和兼容回执。
- 新链采用小范围试验,比较产物、启动、关键路径、诊断与发布行为。
- 每个迁移批次都准备可部署回滚候选与触发条件。
- 完成迁移后撤销旧链权限并保留受控证据,不继续生成无人支持的新版本。
把停止支持公告拆成可执行范围
先把产品、版本、SDK、组件、截止日期和替代路径写成可复核对象,而不是凭页面印象判断。公告版本、发布日期、支持终点、平台范围和迁移说明应与最终候选摘要、版本、环境和操作时间绑定,记录值来自系统回执、测试日志或受控导出。只有这些输入同源,哪些能力将在何时失去修复、兼容或安全支持才有解释力;文件名相似、人员记忆或聊天截图都不能代替对象身份。
保存公告快照并由工具、平台和安全 owner 共同解释。执行前冻结会改变结果的开关、账号属性、时间窗和网络条件,执行中只改变一个变量,执行后保存输入、观察值与判定。每个通过项都要有正向路径和拒绝或失败路径;只记录“成功”会漏掉错误默认值、未授权访问、取消恢复和边界状态。
公告范围或截止日期仍有歧义时先标记 blocked,并把剩余未知写进报告。停止支持不等于已发现漏洞,但意味着未来风险和兼容问题缺少正常支持。这项限制不能靠扩大权限、修改生产导出面或补写未经验证的数字绕过。若后续证据改变,只重开对应记录,不重复执行已经有稳定回执的无关步骤。
| 检查对象 | 输入证据 | 判定动作 | 失败处置 |
|---|---|---|---|
| 产品、版本、SDK、组件、截止日期和替代路径身份 | 公告版本、发布日期、支持终点、平台范围和迁移说明 | 哪些能力将在何时失去修复、兼容或安全支持 | 公告范围或截止日期仍有歧义则阻塞 |
| 同源基线 | 候选摘要与时间窗 | 锁定比较对象 | 来源不明则阻塞 |
| 执行记录 | 保存公告快照并由工具、平台和安全 owner 共同解释 | 保存系统回执 | 只有口头结论则补证 |
| 适用边界 | 停止支持不等于已发现漏洞,但意味着未来风险和兼容问题缺少正常支持 | 限制结论范围 | 不得扩大为全局结论 |
从 App 台账定位实际受影响候选
应用、候选、工具版本、平台和发布渠道要从状态变化而不是最终页面开始核对。把applicationId、候选 SHA-256、工具链、SDK、组件和 owner按发生顺序保留,并为每一步写明触发者、前置条件、候选版本和回执来源。这样可以判断哪些线上 App 必须进入迁移而非仅更新台账发生在读取、计算、传输、系统调度还是业务处理阶段,避免把多个故障压成一句“加固后不可用”。
从发布制品反查真实链路,不按仓库默认版本推测。清单至少包含对象标识、预期、观察、证据引用、责任人和复核状态。自动校验适合发现缺字段、重复记录与不一致结果,最终业务含义仍由熟悉应用的人判断。若依赖第三方平台,还要区分客户端实际状态、平台控制面显示和异步回执的时间差。
验收不能只看操作完成或命令退出码。线上候选无法追到工具链和材料说明链路仍缺少可归因证据;材料缺失时标记 unknown,不能声称当前候选已兼容新链。结论应分成已确认事实、工程判断和适用限制,尤其不能把一次测试通过扩大成所有版本、设备、渠道或客户场景都通过。
| 检查对象 | 输入证据 | 判定动作 | 失败处置 |
|---|---|---|---|
| 应用、候选、工具版本、平台和发布渠道身份 | applicationId、候选 SHA-256、工具链、SDK、组件和 owner | 哪些线上 App 必须进入迁移而非仅更新台账 | 线上候选无法追到工具链和材料则阻塞 |
| 同源基线 | 候选摘要与时间窗 | 锁定比较对象 | 来源不明则阻塞 |
| 执行记录 | 从发布制品反查真实链路,不按仓库默认版本推测 | 保存系统回执 | 只有口头结论则补证 |
| 适用边界 | 材料缺失时标记 unknown,不能声称当前候选已兼容新链 | 限制结论范围 | 不得扩大为全局结论 |
封存旧工具链的可复现基线
处理源码提交、依赖锁、配置、容器、产物和签名输入时,先固定应用身份、候选摘要、平台版本和可变条件。输入摘要、依赖解析、配置摘要、输出摘要和构建回执必须来自同一轮执行,不能把不同设备或不同发布时间的结果拼在一起。证据链闭合后再讨论旧链能否在授权环境重现当前候选,否则重试成功也只能说明环境变了,不能支持技术结论。
在隔离环境复现一次受控构建并记录不可复现点。基线与最终候选使用相同设备、数据准备、账号权限和触发脚本,差异只保留必要变量。遇到失败先保存现场,再按最小范围补测;重装、清数据、切账号或换网络都会重置条件,必须作为新用例登记,不能覆盖原记录。
出现旧链依赖已消失且没有受控镜像或锁定结果,应回到上一份同源输入,比较首次分叉点并保留两侧回执。旧链复现用于基线,不延长供应商支持承诺。临时绕行可以用于恢复业务,但必须记录批准、到期和回退条件,不能让临时措施替代根因验证或变成长期未审计配置。
| 检查对象 | 输入证据 | 判定动作 | 失败处置 |
|---|---|---|---|
| 源码提交、依赖锁、配置、容器、产物和签名输入身份 | 输入摘要、依赖解析、配置摘要、输出摘要和构建回执 | 旧链能否在授权环境重现当前候选 | 旧链依赖已消失且没有受控镜像或锁定结果则阻塞 |
| 同源基线 | 候选摘要与时间窗 | 锁定比较对象 | 来源不明则阻塞 |
| 执行记录 | 在隔离环境复现一次受控构建并记录不可复现点 | 保存系统回执 | 只有口头结论则补证 |
| 适用边界 | 旧链复现用于基线,不延长供应商支持承诺 | 限制结论范围 | 不得扩大为全局结论 |
为新链建立隔离试验与差分矩阵
先把旧链候选、新链候选、设备矩阵与业务用例写成可复核对象,而不是凭页面印象判断。产物差异、启动、关键流程、崩溃、性能边界和签名结果应与最终候选摘要、版本、环境和操作时间绑定,记录值来自系统回执、测试日志或受控导出。只有这些输入同源,新链差异是否在预期范围且可被业务接受才有解释力;文件名相似、人员记忆或聊天截图都不能代替对象身份。
固定输入后逐项比较,只为真实差异新增测试。执行前冻结会改变结果的开关、账号属性、时间窗和网络条件,执行中只改变一个变量,执行后保存输入、观察值与判定。每个通过项都要有正向路径和拒绝或失败路径;只记录“成功”会漏掉错误默认值、未授权访问、取消恢复和边界状态。
新链测试同时改变多个无法归因的变量时先标记 blocked,并把剩余未知写进报告。差分通过不证明安全或性能全面提升。这项限制不能靠扩大权限、修改生产导出面或补写未经验证的数字绕过。若后续证据改变,只重开对应记录,不重复执行已经有稳定回执的无关步骤。
先迁移签名与发布边界外的低风险环节
构建环境、缓存、报告、审批、签名和发布入口要从状态变化而不是最终页面开始核对。把权限 owner、机器身份、审批顺序、签名服务和渠道角色按发生顺序保留,并为每一步写明触发者、前置条件、候选版本和回执来源。这样可以判断哪些依赖先切换、哪些高权限入口最后切换发生在读取、计算、传输、系统调度还是业务处理阶段,避免把多个故障压成一句“加固后不可用”。
按依赖关系排序迁移,生产签名与渠道切换单独批准。清单至少包含对象标识、预期、观察、证据引用、责任人和复核状态。自动校验适合发现缺字段、重复记录与不一致结果,最终业务含义仍由熟悉应用的人判断。若依赖第三方平台,还要区分客户端实际状态、平台控制面显示和异步回执的时间差。
验收不能只看操作完成或命令退出码。高权限入口先切换导致无法回滚说明链路仍缺少可归因证据;这里处理工具生命周期,不评价是否更换供应商。结论应分成已确认事实、工程判断和适用限制,尤其不能把一次测试通过扩大成所有版本、设备、渠道或客户场景都通过。
| 检查对象 | 输入证据 | 判定动作 | 失败处置 |
|---|---|---|---|
| 构建环境、缓存、报告、审批、签名和发布入口身份 | 权限 owner、机器身份、审批顺序、签名服务和渠道角色 | 哪些依赖先切换、哪些高权限入口最后切换 | 高权限入口先切换导致无法回滚则阻塞 |
| 同源基线 | 候选摘要与时间窗 | 锁定比较对象 | 来源不明则阻塞 |
| 执行记录 | 按依赖关系排序迁移,生产签名与渠道切换单独批准 | 保存系统回执 | 只有口头结论则补证 |
| 适用边界 | 这里处理工具生命周期,不评价是否更换供应商 | 限制结论范围 | 不得扩大为全局结论 |
覆盖平台行为变化和第三方 SDK 要求
处理Android 行为变化与 Apple 第三方 SDK 约束时,先固定应用身份、候选摘要、平台版本和可变条件。target SDK、系统行为、隐私清单、签名和 SDK 版本必须来自同一轮执行,不能把不同设备或不同发布时间的结果拼在一起。证据链闭合后再讨论新链是否满足目标平台和分发要求,否则重试成功也只能说明环境变了,不能支持技术结论。
针对目标系统版本和 SDK 清单运行平台门禁。基线与最终候选使用相同设备、数据准备、账号权限和触发脚本,差异只保留必要变量。遇到失败先保存现场,再按最小范围补测;重装、清数据、切账号或换网络都会重置条件,必须作为新用例登记,不能覆盖原记录。
出现目标平台或 SDK 要求没有进入验证矩阵,应回到上一份同源输入,比较首次分叉点并保留两侧回执。平台要求会更新,迁移按指定版本和截止时间验收。临时绕行可以用于恢复业务,但必须记录批准、到期和回退条件,不能让临时措施替代根因验证或变成长期未审计配置。
为每个批次准备真实回滚候选
先把旧候选、新候选、数据、渠道和切换窗口写成可复核对象,而不是凭页面印象判断。回滚候选摘要、可安装性、数据兼容、批准和触发阈值应与最终候选摘要、版本、环境和操作时间绑定,记录值来自系统回执、测试日志或受控导出。只有这些输入同源,故障时能否回到可验证且渠道接受的候选才有解释力;文件名相似、人员记忆或聊天截图都不能代替对象身份。
切换前验证回滚候选可安装、可启动且身份连续。执行前冻结会改变结果的开关、账号属性、时间窗和网络条件,执行中只改变一个变量,执行后保存输入、观察值与判定。每个通过项都要有正向路径和拒绝或失败路径;只记录“成功”会漏掉错误默认值、未授权访问、取消恢复和边界状态。
所谓回滚只是源码分支而非可部署候选时先标记 blocked,并把剩余未知写进报告。回滚受签名、渠道和数据兼容限制,不能只保留旧文件。这项限制不能靠扩大权限、修改生产导出面或补写未经验证的数字绕过。若后续证据改变,只重开对应记录,不重复执行已经有稳定回执的无关步骤。
| 检查对象 | 输入证据 | 判定动作 | 失败处置 |
|---|---|---|---|
| 旧候选、新候选、数据、渠道和切换窗口身份 | 回滚候选摘要、可安装性、数据兼容、批准和触发阈值 | 故障时能否回到可验证且渠道接受的候选 | 所谓回滚只是源码分支而非可部署候选则阻塞 |
| 同源基线 | 候选摘要与时间窗 | 锁定比较对象 | 来源不明则阻塞 |
| 执行记录 | 切换前验证回滚候选可安装、可启动且身份连续 | 保存系统回执 | 只有口头结论则补证 |
| 适用边界 | 回滚受签名、渠道和数据兼容限制,不能只保留旧文件 | 限制结论范围 | 不得扩大为全局结论 |
用代码识别没有迁移闭环的 App
脱敏支持矩阵与应用迁移台账要从状态变化而不是最终页面开始核对。把recordId、应用、支持截止、预期阶段、观察阶段、状态和证据按发生顺序保留,并为每一步写明触发者、前置条件、候选版本和回执来源。这样可以判断每个 App 是否具备验证、回滚和明确 owner发生在读取、计算、传输、系统调度还是业务处理阶段,避免把多个故障压成一句“加固后不可用”。
把公开台账写成 JSON,让校验器拒绝阶段不一致和空证据。清单至少包含对象标识、预期、观察、证据引用、责任人和复核状态。自动校验适合发现缺字段、重复记录与不一致结果,最终业务含义仍由熟悉应用的人判断。若依赖第三方平台,还要区分客户端实际状态、平台控制面显示和异步回执的时间差。
验收不能只看操作完成或命令退出码。支持已到期但迁移阶段仍为空或无人负责说明链路仍缺少可归因证据;代码只检查台账,不下载工具、不运行签名或发布。结论应分成已确认事实、工程判断和适用限制,尤其不能把一次测试通过扩大成所有版本、设备、渠道或客户场景都通过。
import hashlib
import json
import sys
from pathlib import Path
REQUIRED = set([
'recordId',
'applicationId',
'supportEndDate',
'rollbackCandidate',
'expectedStage',
'observedStage',
'result',
'evidenceRef'
])
ALLOWED_RESULTS = set([
'pass',
'blocked',
'mismatch'
])
FORBIDDEN = {'token', 'password', 'privateKey', 'secret', 'customerData'}
def stop(message):
raise SystemExit(message)
def digest(path):
hasher = hashlib.sha256()
with path.open('rb') as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b''):
hasher.update(chunk)
return hasher.hexdigest()
def text(record, field, index):
value = str(record[field]).strip()
if not value:
stop(f'record {index} has empty {field}')
return value
def validate(record, index):
if not isinstance(record, dict):
stop(f'record {index} is not an object')
if FORBIDDEN.intersection(record):
stop(f'record {index} contains forbidden fields')
missing = sorted(REQUIRED - record.keys())
if missing:
raise SystemExit(f'record {index} lacks fields: {missing}')
for field in REQUIRED:
if field != 'supportEndDate':
text(record, field, index)
result = text(record, 'result', index)
if result not in ALLOWED_RESULTS:
stop(f'record {index} has an unsupported result')
expected = text(record, 'expectedStage', index)
observed = text(record, 'observedStage', index)
evidence = text(record, 'evidenceRef', index)
if expected != observed and result == 'pass':
stop(f'record {index} passes despite a mismatch')
if expected != observed and not evidence:
stop(f'record {index} has a mismatch without evidence')
return text(record, 'recordId', index)
if len(sys.argv) != 2:
stop('usage: python validate_eos_migration.py public-checks.json')
source = Path(sys.argv[1]).expanduser().resolve()
if not source.is_file():
stop('public check file is missing')
raw = source.read_bytes()
data = json.loads(raw.decode('utf-8'))
records = data.get('migrations') if isinstance(data, dict) else None
if not isinstance(records, list) or not records:
raise SystemExit('migrations must be a non-empty list')
identifiers = [validate(record, index) for index, record in enumerate(records)]
if len(identifiers) != len(set(identifiers)):
raise SystemExit('record identifiers are duplicated')
summary = {
'inputSha256': hashlib.sha256(raw).hexdigest(),
'records': len(records),
'results': {state: sum(1 for item in records if item['result'] == state) for state in sorted(ALLOWED_RESULTS)},
'status': 'pass',
}
print(json.dumps(summary, ensure_ascii=False, sort_keys=True))切换后关闭旧链并保留证据
处理旧链账号、许可证、服务身份、材料与审计时,先固定应用身份、候选摘要、平台版本和可变条件。禁用状态、归档摘要、读取边界、保留 owner 和复核时间必须来自同一轮执行,不能把不同设备或不同发布时间的结果拼在一起。证据链闭合后再讨论旧链是否可以停止生成候选并收紧访问,否则重试成功也只能说明环境变了,不能支持技术结论。
停止旧链写入和签名能力,受控保留只读证据。基线与最终候选使用相同设备、数据准备、账号权限和触发脚本,差异只保留必要变量。遇到失败先保存现场,再按最小范围补测;重装、清数据、切账号或换网络都会重置条件,必须作为新用例登记,不能覆盖原记录。
出现旧链仍能无审批生成和签名新候选,应回到上一份同源输入,比较首次分叉点并保留两侧回执。归档证据不等于继续授权旧工具链生产使用。临时绕行可以用于恢复业务,但必须记录批准、到期和回退条件,不能让临时措施替代根因验证或变成长期未审计配置。
事实依据与适用边界
以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。
| 本文判断 | 事实或工程依据 | 适用限制 |
|---|---|---|
| 安全发布应保留来源、构建、验证和变更证据,并把供应链风险纳入开发流程。 | NIST SP 800-218 SSDF:安全发布应保留来源、构建、验证和变更证据,并把供应链风险纳入开发流程。 | SSDF 是组织级实践框架,不定义某个 App 加固产品的具体功能。 |
| 构建证明应绑定产物主体、构建者、构建类型、外部参数与依赖材料。 | SLSA Provenance v1.1:构建证明应绑定产物主体、构建者、构建类型、外部参数与依赖材料。 | provenance 只能证明记录的构建过程,不能单独证明运行时安全性。 |
| 供应链证明应把产物摘要与有类型的声明负载绑定,避免报告与候选包错配。 | in-toto Attestation Statement v1:供应链证明应把产物摘要与有类型的声明负载绑定,避免报告与候选包错配。 | 声明格式不保证声明内容真实,仍需可信签名者和门禁核验。 |
| 更新客户端应验证签名、版本、过期时间和一致快照,以识别回滚、冻结和混搭风险。 | The Update Framework specification:更新客户端应验证签名、版本、过期时间和一致快照,以识别回滚、冻结和混搭风险。 | TUF 是更新框架,不直接规定移动端模型或 APK 的业务授权策略。 |
| targetSdk 36 会改变大屏、权限、调度和安全等平台行为。 | Android 16 target behavior changes:targetSdk 36 会改变大屏、权限、调度和安全等平台行为。 | 列表会随平台文档更新,必须按实际 targetSdk 和功能筛选。 |
| 开发者对集成 SDK 的代码和数据行为负责,部分 SDK 还要求签名与 privacy manifest。 | Apple third-party SDK requirements:开发者对集成 SDK 的代码和数据行为负责,部分 SDK 还要求签名与 privacy manifest。 | Apple 列表不覆盖 Android 依赖,也不代表 SDK 通过安全评估。 |
| 工具链停止支持迁移必须同时具备旧链基线、新链验证和回滚候选。 | 工程判断:缺少任一环节都会让差异无法归因或故障无法恢复。 | 无法复现旧链的应用要单独评估,不能伪造基线。 |
| 迁移顺序应把高权限签名与发布切换放在证据和回滚准备之后。 | 工程判断:签名与渠道切换影响应用身份和发布能力,回退成本高于普通构建依赖。 | 具体顺序受企业架构约束,紧急安全事件可采用另行批准的快速路径。 |
工程常见问题
停止支持是否意味着现有 App 立即不安全?
不等于。要核对实际工具版本、组件和候选;但后续漏洞修复、平台兼容和技术支持能力会下降,应在截止前完成迁移。
直接用新工具重加固一次可以吗?
不能省略旧链基线、固定输入、差分测试、签名渠道验证和回滚候选,否则出现差异时无法定位。
旧链已经无法重新安装怎么办?
记录为基线不可复现,尽量从制品、容器、锁文件和历史回执补证,并扩大新链测试与回滚审查。
迁移和更换加固供应商是一回事吗?
不是。这里处理既定工具链生命周期;供应商选择还涉及合同、能力、数据边界和商业评估。
回滚候选只保留旧 APK 够吗?
不够。要验证签名身份、渠道接受、安装升级、数据兼容和业务启动,并明确触发条件与批准人。
提交御盾迁移评估准备什么?
准备停止支持公告、应用与候选台账、工具和 SDK 版本、构建材料、签名渠道、旧链基线、新链差分、设备矩阵和回滚候选。