移动应用 SBOM 怎么做:第三方 SDK、SO 与加固发布门禁指南
移动应用 SBOM 的核心不是导出一张依赖列表,而是让团队能够在每次发布前回答:最终 APK、AAB 或 IPA 中有哪些第三方 SDK、原生 SO、许可证与数据处理责任;它们是否与构建声明一致;加固、渠道构建和重签之后是否发生未被解释的差异。对接入御盾 App 加固的团队,SBOM 应成为原始候选包、加固候选包和最终发布包之间的共同核对底座,而不是发布后才补写的文档。
摘要
一份可用的移动 SBOM 要同时覆盖构建依赖、最终包组件、原生库、许可证、来源、版本、用途、数据处理线索、已知风险和责任人。它不承诺“所有组件绝对安全”,也不替代漏洞扫描、隐私合规、签名验收或真实设备测试;它的价值在于把这些工作指向同一个可追溯对象。加固项目尤其应比较原始包与加固包的差异,明确哪些变化来自保护载体、重签、渠道配置或预期资源处理,哪些变化需要停止发布并复核。
读者对象
- 需要管理 Android/iOS 第三方 SDK、原生库、许可证和发布责任的研发负责人;
- 负责 App 加固、签名、渠道构建、兼容性和 PoC 验收的安全与测试团队;
- 需要审阅供应链、隐私披露、采购材料和上线门禁的产品、法务与交付人员;
- 提供 SDK、算法、音视频、AI、支付或风控能力并需要向客户交付组件说明的团队。
核心结论
- Gradle、CocoaPods、Swift Package Manager 或构建脚本中的声明只是起点,最终发布包才是验收对象。
- 加固不应被当作允许未知组件进入产物的理由;保护前后都要保留可比较的组件、ABI、权限、签名责任和测试范围。
- SBOM 记录“有什么”,发布门禁记录“是否允许发布”,漏洞与合规评审记录“为什么可接受”;三者不能互相替代。
- 组件名称相同不代表版本、来源、许可证、初始化时机和实际行为相同,特别是被转置、打包、裁剪或动态加载的 SDK。
- 对外公开资料应使用类别、方法和模板,不公开客户包体、私有依赖、漏洞利用细节或生产环境信息。
事实依据与脱敏证据
| 序号 | 公开依据 | 可公开事实 | 对工程的要求 | 边界 |
|---|---|---|---|---|
| 1 | Android target API requirements | 目标 API 升级需要核对第三方 SDK 与库并测试核心用例 | 把依赖核对纳入升级与发布流程 | 不等于任何 SDK 自动兼容 |
| 2 | Android SDK user safety guidance | 应用方应了解 SDK 权限、数据及用途 | SBOM 保留用途、权限和责任字段 | 应用方仍需承担披露责任 |
| 3 | Google Play Data safety | 第三方 SDK 的数据收集与共享会影响应用披露 | 将数据类别与接收方纳入组件档案 | 不构成法律意见 |
| 4 | CycloneDX | SBOM 可表示组件、依赖关系与供应链信息 | 输出可交换、可版本化的清单 | 标准文件不替代测试 |
| 5 | SPDX | 许可证与软件包信息可用标准化方式描述 | 为开源组件保留许可证和来源 | 许可证判断需专业复核 |
| 6 | OWASP SCVS | 组件治理需要来源、版本、漏洞和更新控制 | 为每次发布保留差异审查记录 | 公开页不披露攻击路径 |
可信发布证据:SBOM 要能说明“由谁、何时、基于什么生成”
组件清单要进入发布门禁,不能只保留一份没有来源的导出文件。CISA 在 2025 年修订的《Minimum Elements for an SBOM》中补充了 SBOM Author、Tool Name、Generation Context、Component Hash 与 License 等要素,并明确每个软件版本或更新都应关联一份 SBOM;若无法确认依赖信息,必须把“未知”或“有意保留未披露”同“没有依赖”区分开来。对移动应用而言,这意味着原始候选包、加固候选包和最终渠道包各自应有可追溯的清单版本,而不能把上次发布的 Excel 直接沿用到本次构建。
需要特别澄清:该文件讨论了使用签名与既有密钥管理来支持 SBOM 的完整性和真实性,但“作者签名”并不是其列出的最低数据字段。项目可根据交付约束为 SBOM 文件建立签名或验签流程;不能把这一工程选择表述为 CISA 对所有 SBOM 的强制字段,更不能将 App 的发布包签名与 SBOM 文件签名混为一谈。
| 可信字段或状态 | 移动应用中的记录方式 | 进入发布门禁时回答的问题 | 不能由该字段单独证明什么 |
|---|---|---|---|
| SBOM 作者 | 生成方、复核角色与责任边界 | 这份清单由谁生成、谁可说明来源 | 作者名称不保证数据完整 |
| 工具与版本 | 依赖解析、二进制分析或人工补充工具 | 结果能否复现、升级后为何不同 | 工具识别不到不等于组件不存在 |
| 生成上下文 | 构建前、构建中、构建后及目标对象 | 清单对应声明、构建产物还是最终包 | 单一上下文不能覆盖全部组件 |
| 组件 Hash 与算法 | 可交付组件或受控产物的摘要和算法说明 | 原始包、加固包与渠道包的条目是否漂移 | Hash 不判断漏洞是否可利用 |
| 许可证与已知未知项 | 许可证状态、待供应方确认、主动保留 | 哪些问题可发布、哪些需要例外或阻断 | “未知”不是自动通过理由 |
| SBOM 版本与时间 | 与候选版本和变更说明绑定 | 本次发布是否使用了对应清单 | 日期更新不等于完成审核 |
一份适用于发布复核的最小对象关系可以写成:候选包身份 → SBOM 版本 → 生成上下文与工具 → 组件及 Hash → 加固差异说明 → 业务回归范围 → 发布决定或回滚对象。这样做不会暴露客户的私有依赖、生产地址或签名材料,却能让审查者知道“这份清单为何与这次发布有关”。如果项目确有签名或验签要求,应单独记录签名对象、签名者责任、验证时间和密钥管理边界;不要把生产私钥提交给加固或扫描服务。
需要一个不含客户信息的字段起点时,可使用公开的 Trusted Mobile SBOM Example;其中包含 CycloneDX、SPDX、VEX 状态、加固差异和发布门禁的合成样例。它用于解释字段和决策过程,不是对任何真实 App 的供应链、安全或兼容性认证。
技术拆解:移动 SBOM 到底应列什么
移动应用的组件不只有“Java 依赖”。Android 常见对象包括 Gradle 直接与传递依赖、AAR、DEX 中的第三方命名空间、JNI/SO、资源包、动态特性模块、WebView 内容、插件化模块和构建时注入的渠道组件。iOS 则要同时考虑 Swift Package、CocoaPods、XCFramework、静态库、动态 Framework、资源 Bundle、扩展和嵌入式签名对象。若只从一个构建文件导出清单,最终包中的裁剪、合并、重命名、动态下载或手工接入内容就可能遗漏。
建议将每个条目最少记录为:组件显示名称、坐标或来源、版本、平台、用途、是否直接依赖、最终包是否存在、二进制类型、ABI 或架构、许可证、数据处理类别、初始化时机、网络接收方类别、责任人、审查日期和变更理由。对没有公开坐标的闭源组件,应使用项目内部可解释的来源标识,不应编造开源名称或许可证。
| 组件层 | 常见对象 | 需要核对的事实 | 不应从清单直接推导的结论 |
|---|---|---|---|
| 构建层 | Gradle、Pods、SPM、脚本 | 来源、版本、传递关系、锁定文件 | 最终包必然包含全部依赖 |
| 包体层 | DEX、AAR、Framework、资源 | 实际存在、名称空间、模块归属 | 组件一定可被安全替换 |
| 原生层 | SO、XCFramework、静态库 | ABI、架构、来源、加载边界 | Native 一定比托管代码更安全 |
| 运行层 | 动态模块、配置、插件 | 触发条件、下载来源、版本策略 | 每次用户都运行同一组件 |
| 治理层 | 许可证、数据、漏洞记录 | 责任人、审查日期、发布例外 | 已列出就天然合规 |
工程落地:从构建声明走到最终候选包
第一步是冻结候选对象。每次发布都应有唯一的原始候选包、构建编号、依赖锁定文件和配置快照;没有固定对象的 SBOM 只能说明“某个时刻可能存在什么”。第二步是分别导出构建层与包体层清单,再按照来源、模块和版本做比对。无法自动映射的组件不应静默忽略,而应归入“待解释差异”。第三步是补充原生 SO、架构、许可证和数据处理字段,并由对应责任人确认。第四步才是把通过、例外、阻断、回滚候选和复核日期写入发布门禁。
加固接入时,建议保留四组可比较材料:原始候选包的组件清单、目标保护策略摘要、加固候选包的组件清单、最终签名或渠道发布包的组件清单。比较时不应只做文件数量对比,因为保护载体、完整性资源、签名块或预期的包装结构可能产生合理变化;重点是每一项差异都能说明来源、目的、影响范围、验证动作和回滚条件。
| 发布阶段 | 必须保留 | 常见差异 | 通过条件 |
|---|---|---|---|
| 原始候选包 | 包摘要、组件清单、构建与依赖版本 | 构建缓存、传递依赖更新 | 候选对象可唯一定位 |
| 加固候选包 | 策略版本、保护载体、组件差异 | 预期的保护文件与资源变化 | 差异有责任人和说明 |
| 签名/渠道包 | 证书责任、渠道配置、版本号 | 渠道资源与分发配置 | 身份、功能与清单一致 |
| 灰度回滚包 | 回滚目标、变更范围、验证记录 | 旧版本依赖和服务端开关 | 可恢复且未覆盖项明确 |
攻防视角:为什么“看起来正常的依赖”仍要审计
攻击者不一定修改主业务代码,也可能替换一个第三方库、利用过期组件、诱导团队把未经确认的二进制并入渠道包,或在重打包后保留表面相同的名称。SBOM 不能单独阻止这些行为,但它会让“本次候选包与已审查对象是否一致”成为可验证问题。客户端加固可提高篡改关键调用链和批量复制的成本;完整性、签名、服务端风险判断与发布门禁共同决定高价值操作是否继续。任何一个单点信号都不应被宣传为绝对防护。
对于 SDK 供应链,也要避免两种极端:一是把未知 SDK 一律视为恶意,二是只要来自知名供应商就不再核对。真正应问的是该版本是否被项目允许、实际运行在何处、需要哪些权限、处理哪些数据、是否有替代或关闭路径、发布后如何观察异常以及发现问题如何回滚。对第三方 SDK 的数据处理和权限行为,应用运营方仍需要完成自身披露与项目评估。
验收目标、非目标与测量矩阵
本页的主要验收目标是:在一个固定候选版本上,建立可复核的“构建声明—最终包组件—加固差异—发布决定”对应关系,使发布负责人能够确认未知或未解释的组件不会被静默带入正式渠道。它并不以“组件越少越安全”作为目标,也不以扫描结果、文件数量或单次安装成功作为安全结论。组件治理的价值在于减少未知项、缩短定位时间和保留责任链,而不是取代漏洞研究、代码审计、渗透测试、合规审查或真机业务回归。
| 测量目标 | 候选对象 | 最低记录 | 通过口径 | 不应被误用为 |
|---|---|---|---|---|
| 组件可追溯 | 原始候选包与构建锁定文件 | 来源、版本、用途、责任人 | 每个最终包条目可定位到来源或例外 | 全部组件天然安全 |
| 差异可解释 | 原始包、加固包、渠道包 | 差异类别、目的、策略和审核人 | 新增或变化均有说明与验证动作 | 加固不会改变任何文件 |
| 原生覆盖 | SO、Framework、ABI/架构 | 来源、架构、加载与测试范围 | 目标架构均纳入清单 | 已完成所有设备兼容性 |
| 供应链治理 | 第三方 SDK 与开源组件 | 许可证、数据类别、升级记录 | 已知责任和未覆盖项可见 | 自动完成法律或隐私审查 |
| 发布可恢复 | 本次发布与回滚候选 | 阻断条件、例外、回滚对象 | 出现未解释差异可停止或回退 | 任意版本都可无风险回滚 |
具体执行可以分为三层。清单完整性关注有多少条最终组件能找到来源;遗漏条目必须进入待解释队列,而不是以“工具识别不到”结案。差异可解释性关注本次与上一次已批准候选相比新增、删除、替换或版本变动的条目是否有工程原因,例如依赖升级、渠道特性、保护载体或已批准的配置调整。发布可验证性关注这些变化是否经过安装、启动、关键路径、权限、网络接收方和回滚验证。三层中任何一层缺失,清单都不能承担发布门禁角色。
建议把度量结果写成“已覆盖、已发现待复核、未覆盖”三种状态,而不是只输出一个百分比。比如动态模块只在特定功能触发时下载,团队可以如实标记为“本轮未覆盖并列出触发条件”;某个闭源 SDK 没有许可证文件,可以标记为“供应方待补资料”;某项预期保护差异尚未完成业务回归,则应阻断或走有期限例外。这样的结论既不会泄露内部实现,也能让采购、测试和安全团队围绕同一事实做决定。
适配、兼容性与发布门禁的协作方式
移动端组件变化经常同时影响兼容性和安全性。目标 API 升级可能改变权限、后台行为或第三方 SDK 的适配条件;ABI 变化可能使某个原生库在特定设备上不能装载;渠道脚本可能调整资源、清单或开关。SBOM 需要记录“发生了什么”,但是否可以发布仍要由兼容性测试和业务回归决定。推荐在每次候选构建后先冻结组件清单,再生成加固候选,最后在相同业务路径上比较原始包与加固包,避免把不同构建、不同配置或不同渠道的结果混在一起。
对御盾加固项目,发布门禁不需要公开保护实现,也不需要把所有安全策略写进清单。可公开且可交付的最小记录包括:候选版本身份、组件清单版本、策略摘要、加固前后差异说明、签名责任、已测业务路径、未覆盖范围、例外批准与回滚对象。若客户选择接入守界设备风险证据,数据类别和接收方还应按项目范围与守界数据处理清单单独核对,不能从“已加固”推断为默认处理任何设备数据。
SBOM、漏洞情报与 VEX:不要把组件存在直接写成漏洞结论
SBOM 的作用是回答“组件是否存在、来自哪里、对应哪个发布对象”;漏洞情报用于关联已知问题;VEX 则用于说明某项漏洞在特定产品上下文中是受影响、不受影响、已修复还是仍在调查。三者需要连起来,但不能互相替代。即使最终包出现了某个组件,也仍需结合代码路径、构建裁剪、启用配置、ABI、系统版本、服务端条件与关键业务路径判断影响。加固可以提高篡改关键调用链的成本,却不能据此把某个漏洞直接标记为“已修复”或“无影响”。
建议把漏洞影响判断写入独立的 VEX 或例外记录:组件标识、关联漏洞、当前状态、判断依据、复核人、下一次复查条件与发布决定。对证据不足的条目,应保留“调查中”而不是为了降低告警数量写成“不受影响”。这一过程既能减少无效告警,也能避免把供应链报告包装为漏洞修复证明。
风险边界
SBOM 不是漏洞扫描报告,不自动证明组件不存在漏洞;不是许可证意见,不自动解决授权争议;不是隐私政策,不自动完成用户告知;也不是兼容性报告,不能替代安装、启动和关键业务路径测试。御盾 App 加固不改变客户对组件来源、许可证、业务数据和发布决策的责任。公开文章仅提供方法与模板,具体加固策略、组件差异、兼容范围和服务端规则必须在项目 PoC 或正式交付中按候选包复核。
常见误区
最常见的误区是把“生成了一份清单”当作“完成了供应链安全”。没有候选包身份、责任人、差异审查和回滚对象的清单,无法帮助团队在真实发布时判断风险。第二个误区是只把开源库放进清单,而忽略闭源 SDK、原生库、资源包、动态模块和渠道注入组件;真正的验收应围绕最终产物展开。第三个误区是把许可证、隐私、漏洞、兼容性都压缩成一个“安全/不安全”标签。它们有不同的证据和决策人,清单的作用是建立共同对象,而不是替代每一种专项审核。第四个误区是把“可选的 SBOM 签名流程”写成所有项目都已满足的强制字段;应依据交付要求如实记录签名、验签或未启用状态。
另一个容易被忽略的问题是版本漂移。构建脚本没有改动,不代表最终组件不变:远程仓库解析、传递依赖、插件升级、资源处理、渠道配置和动态模块都可能带来变化。因此,团队应锁定依赖并比较每次候选包;无法重复构建或无法解释差异时,不能把上次报告直接复制到本次发布。对于高风险或高价值业务,还应将组件差异与登录、支付、身份核验、内容上传等关键路径回归绑定,确保“清单一致”不掩盖“业务不可用”。
常见问题
只导出 Gradle 依赖树,是否就完成了 SBOM?
没有。依赖树很重要,但它不能保证与最终 APK/AAB 中的 DEX、资源、SO、动态模块和渠道注入内容完全一致。应把构建清单与最终候选包检查结合,并为无法映射的条目建立解释记录。
加固后出现新文件,是否一定是风险?
不一定。保护载体、完整性资源或预期的包装结构可能带来变化。关键是变化必须有策略版本、目的、验证动作和回滚对象;无法解释的二进制、权限、接收方或初始化变化应阻断发布或列为有期限的例外。
SBOM 是否要公开全部组件版本?
不应机械公开。客户可保留内部全量清单;对外材料宜说明治理方法、数据类别和公开边界。私有组件、未修复漏洞、生产依赖、包体摘要和敏感架构信息应按项目安全要求处理。
SBOM 中发现 CVE,是否就说明本次 App 必须下架?
不一定。组件与漏洞的关联需要进一步判断漏洞路径是否存在、是否可达、是否受当前配置和运行环境影响。应保留 VEX 或等价的影响判断记录;若暂时无法判断,应标记为调查中并按风险等级决定阻断、例外或加急复核,而不是直接宣称无影响。
组件清单和 App 加固有什么关系?
组件清单解决“候选包里有什么、是否可追溯”,App 加固解决“关键代码和运行时链路如何提高篡改与复制成本”。二者可在同一发布门禁中协作,但不是彼此替代,也不要求客户默认同时购买。
内链与后续资料
需要把组件、签名、策略和测试结果组织成上线门禁时,可继续阅读APP加固发布门禁指南。需要核对加固前后 SDK、SO、权限和最终包差异时,应阅读加固后第三方 SDK 差异核对方法。可复制的字段清单与发布表见移动应用组件清单与发布模板。评估御盾 App 加固能力与项目边界,请进入御盾 App 加固产品页或提交封闭兼容性验证申请。
结语
移动应用的供应链治理不应在出现漏洞、上架异常或客户追问后才开始。将构建依赖、最终包组件、SO、数据处理、许可证、加固差异和发布责任放进同一个可追溯链路,团队才能在速度与可控之间取得平衡。SBOM 的目标不是制造更多文档,而是在每一次发布前让“这是谁的组件、为什么在包里、变化是否被验证、出现问题如何恢复”都有可复核答案。
建议从一个候选版本开始,不追求一次性补齐历史所有发布包。先把高价值业务、变化频繁的 SDK、原生库和即将上线的渠道纳入范围;待流程稳定后,再逐步扩展到完整产品线。每次扩大范围都应记录新增覆盖与未覆盖项,避免用“已建立 SBOM”掩盖仍未核对的依赖和运行路径。
清单应随版本持续维护并复核。
需要针对自己的 App 验证加固策略?
提交项目平台和当前攻防问题,安全工程师会按业务复杂度安排人工审核。完整技术档案可在申请后补充。