先看结论与判断条件

  • 交付物用文件摘要、应用标识、版本、构建来源、加固配置身份和签名阶段识别,不能只写最终安装包。
  • 客户输入责任与服务处理责任分别列出,双方配合必须拆成有负责人、截止条件和证据的具体动作。
  • 验收矩阵区分产物身份、加固范围、功能回归、安全控制、性能观察和渠道发布,不允许一项通过替代全部接受。
  • SOW 应写清测试范围、设备和环境、失败条件、已知限制与未测项,不能把没有证据的场景默认为通过。
  • 签名、上架和业务发布决定通常处于不同控制边界,需要明确谁持有密钥、谁验签、谁批准进入渠道。
  • 缺陷、变更、重测、证据保留和退出条件都要提前定义,避免项目结束时再争论交付是否完整。

先把 SOW 当成技术边界清单,而不是宣传说明

软件加固服务的 SOW 或工作说明书首先要回答对象、动作、产物、责任和完成证据。只写提供代码保护、提升安全性或完成加固,无法让任何一方判断哪一个候选被处理、哪些模块进入范围、哪些环境接受测试、失败如何处置,也无法区分技术交付与正式发布。技术条款应使用可观察名词和可复核动作,商业与法律责任仍由有权角色审定。

条款结构从搜索问题出发通常包含输入、处理范围、输出、验收、签名、变更、缺陷、限制和退出。每个部分都有唯一负责人,双方共同负责只能作为协作关系,不能成为没有动作和日期的模糊字段。例如客户提供可构建的 release 候选,服务方按批准配置处理,双方在指定矩阵上验证,这三个动作必须分别说明进入条件与回执。

SOW 不应预先写入未核实的防护强度、兼容率、性能数字、攻击阻断或商店通过承诺。可以约定评估目标、测试方法、证据格式和接受权限,但结论要来自同一候选的实际回执。若目标是降低逆向分析便利度,也要写清采用哪些可验证控制和边界,而不是把一个抽象安全等级当作已经交付的事实。

SOW 技术条款的最小结构
条款必须回答主要负责人完成证据
输入提交什么候选输入所有者摘要与清单
处理哪些范围与配置技术执行方配置身份
输出交付哪些制品交付负责人产物索引
验收测什么怎样判定批准人测试回执
发布谁签名谁上架发布负责人签名与渠道记录
退出材料与权限怎样收尾双方指定角色移交撤权记录

输入责任要覆盖可处理性、真实性和敏感边界

输入清单至少包括应用标识、版本、release 变体、目标渠道、未签名或已签名状态、文件摘要、构建来源、原生库、符号与映射、保护配置、兼容要求和测试账号边界。客户需要确认输入有权交付、来源可追踪且满足约定前提;执行方需要确认收到的文件摘要一致,并在开始处理前报告缺失项。双方不能用文件名相同推断内容相同。

敏感输入要单独分类。生产私钥、真实用户数据、可用令牌和内部凭据不应因为项目需要配合就自然进入交付包。SOW 应规定允许传递的最小材料、传输方式、处理区域、访问角色、日志字段、保留例外和清理回执。确需特殊材料时,另列批准、用途、时限和撤销条件,不能把双方提供必要信息写成无限授权。

输入不合格时应暂停并给出可操作问题,而不是由执行方悄悄修补。比如版本信息与候选不一致、缺少目标 ABI、构建本身无法启动、签名状态不符合约定或测试环境未准备,都应进入输入缺陷清单。修正后生成新的输入摘要和接收回执,原输入保留状态但不冒充已处理候选。

输入接收清单
输入字段提交方责任接收方责任失败处理
候选摘要计算并提交重新计算比对拒绝同名替换
应用版本声明真实变体读取候选核对标记冲突
签名状态说明签名阶段验签并记录停止错误顺序
处理范围指定业务模块确认技术可行性提出排除项
测试条件准备账号和服务确认环境可用登记阻塞
敏感材料最小化并审批限制访问与日志拒绝未授权材料

交付物必须绑定候选身份与处理上下文

交付物不只是一个 APK、AAB、IPA 或安装包文件。完整索引应包含输出摘要、输入摘要、应用和版本、处理工具或构建者身份、处理类型、批准配置、依赖材料、签名前后状态、生成时间、测试范围、已知限制和报告入口。任何报告、mapping、符号或测试结果都要引用同一候选身份,避免漂亮报告和最终发布包实际上属于不同构建。

SLSA Provenance v1.1 用产物主体、构建者、构建类型、外部参数和依赖材料描述构建证明。加固步骤未必完全等同普通编译,但 SOW 可以借鉴这种绑定方式要求交付上下文。in-toto Attestation Statement v1 又以 subject 摘要和有类型的 predicate 组织声明。格式只解决关联结构,仍需可信签名者和实际摘要复算。

NIST SP 800-218 SSDF 强调安全开发中的来源、构建、验证、变更和供应链风险。合同附件可把这些实践转成可验收字段,例如提交身份、处理版本、变更记录、验证负责人和例外批准。SSDF 不定义某个加固产品功能,因此不能直接把遵循框架写成防护能力已达标,而应把具体过程回执列为交付物。

交付索引需要绑定的对象
对象唯一标识验收动作边界
输入制品文件摘要与接收回执比对不代表输入正确
输出制品文件摘要独立重新计算不代表兼容通过
处理配置版本与配置摘要匹配批准单不公开敏感细节
构建者受信身份验证证明签名不保证无误操作
测试回执候选与范围检查身份绑定只覆盖已测项
限制清单问题编号确认责任和状态不得省略未知项

保护范围和排除项要能驱动配置与变更

处理范围应从业务对象和技术对象两端描述。业务对象包括授权、支付、关键算法、凭据处理和高价值资源;技术对象包括包、类、方法、原生库、资源和入口。SOW 不需要公开可被滥用的细粒度保护规则,但要让双方知道哪些对象被纳入、哪些因反射、序列化、动态加载或平台限制被排除,以及排除会留下什么验证责任。

OWASP MASVS-RESILIENCE 将抗逆向和抗篡改放在纵深防御边界中。条款可以把相应控制域作为验收参考,却不能写成达到该目录就替代服务端授权、签名、更新和完整发布链。每项韧性控制需要候选级证据、适用条件和限制;没有运行或静态证据时,只能记为计划目标,不能登记已完成。

范围变更应产生新的配置身份、影响分析和重测范围。客户新增模块、修改构建、升级依赖或变更签名流程,执行方调整保护规则、处理版本或兼容策略,都可能使既有回执失效。SOW 要定义触发变更的事件、谁评估、谁批准、如何报价或排期,以及旧候选的接受状态。不能在项目末尾用已沟通替代正式变更记录。

保护范围条款的写法
字段可核对表达模糊表达变更触发
业务对象列出关键流程核心模块业务流程改变
技术对象包类库和入口主要代码代码结构改变
排除项对象原因和风险按实际情况限制解除或新增
配置身份版本与摘要默认配置任何规则调整
验证范围场景设备和边界基本测试目标环境变化
未知项责任人和截止条件双方确认取得新证据

验收矩阵应分开身份、功能、安全与发布决定

验收第一层是产物身份,确认输入、输出、配置、签名状态与报告互相绑定;第二层是功能回归,验证启动、登录、网络、存储、推送、支付和业务关键路径;第三层是安全控制,检查约定的加固对象和韧性控制证据;第四层是发布准备,确认签名、渠道、服务依赖和业务批准。任何一层通过都不能自动替代其他层。

OWASP MASVS overview 把移动安全控制分到存储、密码学、认证、网络、平台交互、代码质量和韧性等领域。SOW 可以从项目相关领域选择验收问题,并写出测试方法、设备、期望结果和证据。标准提供控制域而不是项目结论,未被选择或未测试的控制应作为排除或未测项保留,不得用符合 MASVS 这样的总括句直接接受候选。

验收条件必须包含失败如何记录。某个设备崩溃、接口返回异常、签名身份不符、报告无法绑定候选或安全控制无证据时,状态应是 failed、blocked 或 accepted-with-known-limit,并由唯一批准人处理。允许带限制接受时,要写清适用版本、受影响范围、缓解措施和后续责任,不能把待修缺陷从最终报告中删除。

加固候选验收矩阵
验收域输入证据通过条件不能推出
产物身份摘要与索引全部对象匹配功能正确
功能回归设备和用例回执约定场景通过未测设备通过
安全控制静态与运行证据指定控制有证据攻击已阻断
性能观察同候选基线满足约定阈值所有负载稳定
签名验证证书和签名回执身份与政策匹配商店审核通过
业务接受风险和限制清单授权人签收自动批准发布

签名、上架与业务发布必须明确由谁控制

Android publish your app 指出发布前需要配置 release 变体、构建签名产物、完成测试并准备应用依赖服务,上传只是其中一个环节。SOW 因此要说明加固输出是未签名候选还是可发布候选,生产签名由谁持有和执行,验签由谁完成,商店材料和服务端配置由谁准备。没有明确授权时,技术交付不能被解释为已批准上架。

生产私钥、上传密钥、证书和渠道账号不应笼统归入客户提供必要材料。优先让执行方不接触生产私钥,由客户或受控发布系统在所有会改写二进制的步骤完成后签名。若项目确需特殊签名协作,另列最小权限、调用方式、审批、日志、轮换、撤销和事故责任,并禁止在交付报告中保存私钥或口令。

发布接受还依赖商店政策、后端服务、隐私材料、账号状态和业务窗口,这些可能不在加固服务控制范围内。条款要区分提供发布所需技术产物、协助排查技术问题和对商店最终决定负责。通用发布指南不能证明某个实际候选可直接上架,只有真实渠道回执才能记录上传或审核状态。

缺陷、重测和退出要在开始前约定闭环

缺陷条款至少包含严重度判定人、复现材料、候选摘要、受影响范围、首次响应、修复或解释、重测责任和关闭批准。为了避免无证据争论,复现包不应携带真实用户数据或凭据,日志应脱敏并使用关联号。无法复现不等于没有问题,应记录已尝试环境、缺失条件和下一步责任。

变更与缺陷要分开。输入代码或依赖发生变化、客户扩大范围、执行工具或配置主动升级属于变更;同一批准输入和配置下未满足约定条件才可能是缺陷。分类影响重测范围、排期和接受状态,但不能由任何一方单方面通过改名字规避责任。争议时回到候选、配置、范围和回执四个固定对象。

退出条件包括交付物移交、访问撤销、临时数据处置、保留证据接收人、未决缺陷、签名能力边界和后续联系人。退出不等于删除所有历史材料,保留与销毁应按批准政策及活动 hold 执行;也不等于无限保留供应方访问。SOW 应列出可逆和不可逆动作的批准人,完成后提供撤权与移交回执。

缺陷与变更的分流
事件分类问题处理产物关闭权限
功能崩溃同一输入配置是否复现缺陷和设备回执验收批准人
新增业务模块范围是否扩大变更单和新配置范围批准人
依赖升级输入身份是否改变影响分析和重测技术负责人
规则调整配置身份是否改变新候选和证据双方指定角色
商店拒绝原因是否属于技术范围渠道回执与分流发布负责人
项目退出未决项是否有归属移交撤权清单唯一最终批准人

用代码检查 SOW 结构中的模糊责任和缺失证据

下面的 Python 示例读取公开安全的结构化 SOW 样例,检查交付物、唯一批准人、接受证据、排除项、变更机制和退出条件。它拒绝在字段中放入私钥、令牌、密码或客户数据,也会把 owner 写成双方、共同负责或待定视为模糊责任。代码只验证结构和明确性,不生成合同、不提供法律意见,也不替任何一方接受实际交付。

每个 deliverable 都需要 id、owner、artifactRef、acceptanceEvidence、exclusions 和 status。artifactRef 应是公开安全的索引或摘要引用,不是内部地址;acceptanceEvidence 要指向回执种类,不是符合要求这样的自我结论。唯一 approver 负责最终技术接受,但其授权来源与法律效力需要组织另行确认。

实际使用时可增加服务级别、费用、适用法律和争议解决等字段,但这些属于法务与商业审查范围。技术校验器关注的重点是任何完成状态都有证据、排除项没有被隐藏、变更和退出有明确机制。若规则失败,应补充对应字段或走显式例外批准,不能删除检查来制造通过记录。

校验软件加固 SOW 的技术交付结构
import json
import re
import sys
from pathlib import Path

REQUIRED_ROOT = {
    'title',
    'approver',
    'deliverables',
    'changeControl',
    'exitConditions',
}
REQUIRED_ITEM = {
    'id',
    'owner',
    'artifactRef',
    'acceptanceEvidence',
    'exclusions',
    'status',
}
FORBIDDEN = {'privateKey', 'token', 'password', 'customerData'}
AMBIGUOUS_OWNER = re.compile(r'^(both|joint|tbd|双方|共同负责|待定)$', re.I)
ALLOWED_STATUS = {'planned', 'blocked', 'delivered', 'accepted'}

def stop(message):
    raise SystemExit(message)

def text(value):
    return str(value).strip()

def validate_item(item, index, approver):
    if not isinstance(item, dict):
        stop(f'deliverable {index} is not an object')
    leaked = FORBIDDEN.intersection(item)
    if leaked:
        stop(f'deliverable {index} contains forbidden fields')
    missing = sorted(REQUIRED_ITEM - item.keys())
    if missing:
        stop(f'deliverable {index} lacks fields: {missing}')
    owner = text(item['owner'])
    if not owner or AMBIGUOUS_OWNER.fullmatch(owner):
        stop(f'deliverable {index} has an ambiguous owner')
    if item['status'] not in ALLOWED_STATUS:
        stop(f'deliverable {index} has invalid status')
    if not text(item['artifactRef']):
        stop(f'deliverable {index} has no artifact reference')
    if not isinstance(item['exclusions'], list):
        stop(f'deliverable {index} exclusions must be a list')
    evidence = text(item['acceptanceEvidence'])
    if item['status'] == 'accepted' and not evidence:
        stop(f'deliverable {index} is accepted without evidence')
    if item['status'] == 'accepted' and not approver:
        stop(f'deliverable {index} is accepted without approver')
    return item['id']

if len(sys.argv) != 2:
    stop('usage: validate_sow.py sow.json')
source = Path(sys.argv[1])
if not source.is_file():
    raise SystemExit('SOW file is missing')
sow = json.loads(source.read_text(encoding='utf-8'))
if not isinstance(sow, dict):
    raise SystemExit('SOW root must be an object')
missing_root = sorted(REQUIRED_ROOT - sow.keys())
if missing_root:
    raise SystemExit(f'SOW lacks root fields: {missing_root}')
if FORBIDDEN.intersection(sow):
    raise SystemExit('SOW contains forbidden root fields')
approver = text(sow['approver'])
if not approver or AMBIGUOUS_OWNER.fullmatch(approver):
    raise SystemExit('SOW must name one approval role')
for field in ('changeControl', 'exitConditions'):
    if not text(sow[field]):
        raise SystemExit(f'SOW has empty {field}')
items = sow['deliverables']
if not isinstance(items, list) or not items:
    raise SystemExit('SOW deliverables are missing')
ids = [validate_item(item, index, approver) for index, item in enumerate(items)]
if len(ids) != len(set(ids)):
    raise SystemExit('SOW contains duplicate deliverable ids')
print(json.dumps({'deliverables': len(ids), 'status': 'pass'}))

最终接受只关闭已核实的技术事项

最终评审逐项打开交付索引、候选文件、配置身份、测试回执、签名状态、限制清单、缺陷记录和变更单。接受表示指定批准人已核实约定证据,不表示从此没有漏洞、所有攻击均被阻断、全部设备兼容或商店一定通过。无法核实的事项保留 blocked 或 unknown,并给出责任人和下一步。

业务接受与技术接受应分别记录。技术团队可以确认候选符合约定的身份、范围和测试条件,业务负责人还要决定剩余风险、发布时间、用户影响和渠道策略。若使用带限制接受,限制必须跟随候选进入发布决策,不能只留在项目聊天里。后续任何构建、依赖、配置或签名变化都应触发适当重验。

准备项目时,可先整理输入清单、保护范围、候选身份、设备矩阵、签名流程、证据格式、缺陷分级和退出要求,再参考本站的软件加固交付报告阅读方法核对回执。需要继续确认技术 SOW 边界,可携带这组材料通过御盾中央平台提交申请。最终合同文本、承诺和法律责任应由具备授权的业务与法律角色审定。

事实依据与适用边界

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

本文判断事实或工程依据适用限制
安全交付应保留来源、构建、验证和变更证据,并纳入供应链风险管理。NIST SP 800-218 SSDF 提供组织级安全软件开发实践与供应链风险方向。SSDF 不定义任何具体加固产品的功能、报价、服务承诺或项目验收结论。
产物证明可把制品主体与构建者、构建类型、外部参数和依赖材料绑定。SLSA Provenance v1.1 定义 provenance 模型及主要字段语义。provenance 记录不能单独证明运行时安全、配置正确或项目全部验收通过。
供应链声明应以摘要标识产物,并使用明确类型组织声明负载。in-toto Attestation Statement v1 定义 subject、predicateType 与 predicate 的结构。声明格式正确不保证内容真实,仍需可信签名者、策略和候选摘要复算。
移动安全验收需要按存储、密码学、认证、网络、平台交互、代码质量和韧性分别取证。OWASP MASVS overview 描述移动应用安全验证标准及其控制领域。标准给出控制域,不提供御盾或其他具体产品的候选级验收结论。
移动端抗篡改与抗逆向属于纵深防御控制,不能替代服务端授权和完整发布链。OWASP MASVS-RESILIENCE 描述逆向和篡改韧性控制领域。控制目录不证明某个候选达到特定防护强度,也不证明攻击已被阻断。
Android 发布前需要准备 release 变体、签名产物、测试和应用依赖服务。Android publish your app 描述构建、签名、测试与发布准备的主要环节。通用发布指南不证明某个交付包可直接上架,也不替代具体商店的真实审核。
软件加固 SOW 应把技术交付、业务接受和发布决定分成不同批准记录。工程判断:三者依赖的证据、责任人和剩余风险不同,合并会造成越权接受。具体授权、合同效力和法律责任由组织与专业法律角色审定,文章不提供法律意见。
完成状态应绑定候选摘要、配置身份、测试证据、已知限制和唯一批准人。工程判断:明确对象和证据能减少报告错配、模糊责任和无依据的完成声明。结构完整不保证事实真实,仍需独立核验产物、身份、签名和测试回执。

工程常见问题

软件加固 SOW 只写交付 APK 或 IPA 为什么不够?

文件名无法证明候选身份、输入来源、处理配置、签名状态、测试范围和限制。交付索引应使用摘要把产物、配置、报告和回执绑定起来。

验收通过是否等于应用可以直接发布?

不等于。技术验收确认约定证据,业务接受决定剩余风险,发布还依赖签名、渠道、服务配置、政策审核和授权窗口,应分别记录。

SOW 能否承诺加固后绝对无法逆向或篡改?

不应写无法验证的绝对承诺。可以约定具体控制、测试方法、适用限制和证据,但纵深防御不能替代服务端授权,也不能证明所有攻击被阻断。

双方共同负责可以作为交付物 owner 吗?

协作可以涉及双方,但每个动作应有唯一主责角色、配合动作和完成证据。只写双方共同负责会让阻塞、批准和退出时无人作最终决定。

输入代码或依赖改变后,原验收是否仍然有效?

不能默认有效。应识别变更是否影响候选、配置、签名或测试范围,生成新身份并执行对应重测,再由批准人决定原接受状态。

向御盾中央平台提交技术 SOW 评估前准备什么?

准备输入与敏感材料清单、保护范围、排除项、候选身份、签名流程、设备矩阵、验收证据、缺陷分级、变更机制、唯一批准角色和退出条件。

想用自己的 App 验证?

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

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