先看结论与判断条件
- 内置模型仅继承应用签名,无法证明模型资产源自特定发布者,反编译即可暴露结构。
- 动态下载 Core ML 模型缺乏框架级签名校验,必须叠加更新框架或自定义摘要检查机制。
- 编译缓存目录的摘要与 manifest 比对能识别替换,但前提是比对发生在可信执行环境且排除后置替换窗口。
- Core ML 加密算子强化静态提取门槛,但模型在被框架加载后仍可被具备相同授权的分析工具读取。
- 版本回滚与降级攻击需要更新框架提供快照、过期时间与密钥轮转,TUF 仅给出可参考的元数据校验模式。
- 供应链证明把制品摘要与声明负载绑定,可帮助定位来源;只有签名者身份和验证规则可信时,该记录才适合参与授权判断。
- manifest 文件若未签名,攻击者可同步篡改模型与摘要列表,使本地完整性校验彻底失效。
- 动态模型下载应验证来源与响应内容。是否使用证书固定或独立内容签名,要结合威胁模型和更新架构决定,不能把单一手段写成通用前提。
内置模型与动态下载的边界选择
内置模型跟随应用包分发时,其安全属性完全继承应用签名和包完整性保护机制。攻击者若能从设备提取应用包或利用越狱文件系统权限,即可获得完整模型文件。因此,内置模型只是将暴露风险延迟到安装之后,并没有独立的密钥控制或摘要验证能力来隔离模型资产。
动态下载模型消除了应用包体尺寸的瓶颈,允许独立于应用版本进行迭代。然而下载流程基于 URLSession 完成,本身没有强制要求响应体必须携带数字签名。文档指出编译由系统处理,但未声明下载阶段对 manifest 或证书固定存在依赖,这留下了中间人替换的风险窗口。
决策时需权衡具体场景:内置便于离线场景且编译不会因弱网环境而失败,但更新需整包发布;动态下载允许独立更新模型,但需要额外实施来源认证。两者必须分别确定静态存储与网络传输两个阶段的最低保护要求,不能混为一谈。
对于高敏感业务,内置模型应视为公开资产,仅依靠应用签名不足以防止逆向分析。动态模型则必须在网络层和应用层双重加固,否则下载过程等同于明文传输。工程上应根据数据敏感度决定模型交付形态,而非单纯追求包体优化。
| 交付方式 | 完整性保护依赖 | 来源认证支持 | 关键局限 |
|---|---|---|---|
| 内置模型 | 应用签名与 Bundle 校验 | 依赖开发者证书与 Store 审查 | 无独立密钥控制,提取无额外障碍 |
| 动态下载(无认证) | TLS 传输层校验 | 仅服务器证书 | 中间人替换风险未从框架消除 |
| 动态下载(附加摘要) | 自定义摘要比对与 TLS | manifest 钥匙或更新框架签名 | 摘要加载过早或过晚均有时间窗口 |
| 编译缓存复用 | 本地文件系统权限 | 无额外认证 | 可被替换且无法区分正常复用的编译任务 |
- 是否明确内置模型在越狱环境下的不可保护性?
- 动态下载是否实施了域名证书固定或公钥锁定?
- 是否在编译前对响应体进行了独立的完整性校验?
- 是否对下载失败或校验失败的模型阻断了回退路径?
动态下载模型的前置安全条件
动态下载模型的入口是网络请求,这是整个链条中最脆弱的环节。若应用未对模型下载的域名实施证书固定,仅依赖系统信任库,则攻击者可通过中间人替换整个模型文件,进而控制预测流程。这并非 Core ML 的缺陷,而是网络层未提供对象级别验证时的通用问题。
根据官方文档描述,下载完成后系统会调用编译步骤。但该编译依赖文件头格式合格,不要求文件与开发者预期的摘要一致。于是恶意模型只要结构合法就能被加载并执行推断,给任意代码执行留下潜在突破点,因为模型结构可能包含恶意构造的计算图。
动态下载链路可先限制允许来源,再在使用缓存前验证内容摘要或独立签名,并把编译产物与可信 manifest 对照。具体项目可按现有信任根选择校验组合;当身份或摘要无法确认时,加载流程应进入明确的失败或回退分支。
此外,必须验证响应头的 Content-Length 与实际接收字节数是否一致,防止截断攻击。如果服务器支持范围请求,还需确保分段下载的拼接完整性。任何网络层面的异常都应触发重试或报错,而不是静默使用部分损坏的模型文件。
- 是否固定模型下载域名的证书或公钥以防止中间人攻击?
- 是否在编译前校验响应的 Content-Length 与实际大小一致?
- 是否在编译完成后比对产物摘要与期望值以确认未被篡改?
- 是否对下载失败或校验失败模型阻断回退路径避免使用旧版?
编译缓存与产物完整性的验证
Core ML 将下载或内置的 .mlmodel 编译为 .mlmodelc 目录,后续推理会使用其中的模型描述、权重和元数据。若本地编译缓存可被替换,而应用又不校验其来源与摘要,源模型阶段的检查就无法覆盖这一后续文件,因此缓存目录也应纳入完整性验证。
核对编译产物的手段是计算目录内容摘要并与可信 manifest 比对。但编译缓存的设计允许系统在不同时机重新编译,导致摘要可能合法变化。因此,必须确保一致性检查对象是可复现的特定文件,例如 mil 文件或编译后的 weight.bin,而非整个目录。
Apple Core ML 文档未提供编译产物摘要标准接口,开发者需自行选择稳定输入集。工程可锁定模型编译输出中的特定文件名与字节序,排除索引缓存、浮动临时文件。失败条件包括:摘要比对后短时间窗口内又被替换、或构建环境不同导致差异。
验证逻辑必须在模型加载前执行,且运行在受保护的上下文中。如果验证过程本身可被 Hook 或绕过,则整个机制失效。建议在 native 层实现哈希计算,并对比存储在只读区域或远程签名的期望值,减少内存中被篡改的风险。
| 验证对象 | 稳定性 | 实施难度 | 抗篡改能力 |
|---|---|---|---|
| 整个.mlmodelc 目录 | 低,随编译环境变化 | 低 | 弱,易因正常重编译误报 |
| 特定 model.mil 文件 | 高,结构定义稳定 | 中 | 强,直接反映模型逻辑 |
| 权重二进制文件 | 高,数据内容固定 | 中 | 强,防止权重投毒 |
| 元数据 plist 文件 | 中,可能含时间戳 | 低 | 弱,易被忽略或伪造 |
import hashlib
import json
import os
import sys
import argparse
def hash_file(file_path, algo='sha256'):
h = hashlib.new(algo)
with open(file_path, 'rb') as f:
while True:
chunk = f.read(8192)
if not chunk:
break
h.update(chunk)
return h.hexdigest()
def load_manifest(manifest_path):
if not os.path.isfile(manifest_path):
print(f"错误:Manifest 文件不存在:{manifest_path}", file=sys.stderr)
sys.exit(2)
with open(manifest_path, 'r', encoding='utf-8') as mf:
return json.load(mf)
def check_compiled(manifest_path, compiled_dir, algo='sha256'):
manifest = load_manifest(manifest_path)
errors = []
for entry in manifest.get('models', []):
name = entry.get('name')
expected = entry.get('sha256')
if not name or not expected:
errors.append(f"跳过条目,缺少 name 或 sha256: {entry}")
continue
target_file = os.path.join(compiled_dir, f"{name}.mlmodelc", "model.mil")
if not os.path.isfile(target_file):
errors.append(f"缺失候选文件:{target_file}")
continue
actual = hash_file(target_file, algo)
if actual != expected:
errors.append(f"摘要不匹配 {name}: 期望 {expected}, 实际 {actual}")
if errors:
for e in errors:
print(e, file=sys.stderr)
sys.exit(1)
else:
print("所有模型摘要核验通过")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='校验 Core ML 编译产物完整性')
parser.add_argument('--manifest', required=True, help='模型 manifest JSON 路径')
parser.add_argument('--compiled-dir', required=True, help='编译产物父目录')
args = parser.parse_args()
check_compiled(args.manifest, args.compiled_dir)
模型签名与版本授权
本文所引 Apple 资料没有给出与 App 代码签名等同的模型作者验证链。模型来源认证通常由应用的下载、更新或资源封装流程承担,因此需要明确期望签名者、可信公钥和失败处理,不能假设操作系统会替业务完成这些检查。
版本授权重点在于防止回滚攻击:攻击者提供旧版本模型以利用已知的弱推断或后门。TUF 规范要求在元数据中声明版本号、过期时间与私钥轮转机制,从而使客户端能拒止过旧或过期的模型。但该机制依赖每次启动时的在线查询或本地可信时钟。
在移动环境,必须将版本元数据嵌入 manifest 并让客户端在编译前校验版本单调递增或符合最小允许集。若仅由服务端下发版本号而客户端不执行本地单调递增判断,则攻击者可通过重放旧响应完成版本级降级,绕过最新的安全补丁。
实施版本控制时,需在本地持久化当前最高版本号,并在每次更新时严格比较。如果新版本号小于或等于本地记录,应拒绝加载。同时,manifest 本身必须签名,防止攻击者修改版本号字段来欺骗客户端接受旧模型。
| 机制 | 依赖元数据 | 回滚防护强度 | 实施复杂度 |
|---|---|---|---|
| manifest 内最低版本 | JSON 明文版本号 | 中,可被重放旧 manifest | 低 |
| 更新框架实施快照与过期 | 签名元数据 | 较高,结合密钥轮转 | 较高 |
| 服务端强制参数递增 | 请求附带客户版本 | 中,依赖服务端状态 | 中等 |
| 证书固定 + 时效控制 | URL 证书绑定 | 防中间人重定向,非防回滚 | 低 |
- manifest 文件是否包含数字签名以保护版本信息?
- 客户端是否本地存储并校验版本号的单调递增性?
- 是否设置了元数据的过期时间以防止长期重放?
- 是否在离线模式下限制了可接受的最低模型版本?
Core ML 加密的保护强度与运行时可见性
根据官方文档,开发者可为模型归档或编译产物生成加密密钥,模型在使用前需要对应的解密密钥。这主要提高了静态存储和传输的安全门槛,防止直接二进制拷贝后解析。但这仅仅是增加了静态分析的难度,并非绝对防御。
但是,模型在被 Core ML 框架解密并加载后,其权重和计算图必须存在于进程地址空间。具备 root 或相同沙盒授权的调试工具、动态注入手段以及模型调优工具都可能通过内存扫描或框架接口读取部分或全部结构,这是物理执行的必然结果。
因此,加密提供的保护边界止于授权执行之前。对于需要保护商业模型机密或合规数据的场景,必须在授权分发、设备完整性认证和运行时反调试等多个层面叠加控制,不能假设加密能消除推理阶段后的全部信息泄露风险。
密钥管理同样关键,硬编码在代码中的密钥极易被逆向提取。应利用硬件安全模块或系统密钥链存储密钥,并结合设备指纹进行绑定。即便如此,一旦设备被攻破,内存_dump_依然可能暴露解密后的模型参数。
- 密钥是否与设备或用户绑定,或者关联到硬件密钥管理器?
- 模型解密后在内存中是否避免持久化明文权重?
- 是否有反调试和完整性检查防止动态提取?
- 加密是否仅保护归档文件,编译缓存是否得以豁免?
模型 manifest 与摘要核对的失败条件
manifest 是信任根基,其内容决定编译后产物应具备的摘要。如果 manifest 自身未经签名或传递过程未受保护,则攻击者可修改 manifest 同时替换模型文件,使摘要比对失效。因此 manifest 的完整性校验优先级高于模型文件本身。
in-toto 声明把产物摘要放进带类型的声明负载,并由签名者发布。映射到模型分发时,manifest 除摘要外还要能追溯签名者与适用版本;客户端验证的是签名和当前模型是否匹配,声明格式本身并不保证内容真实。
在核对流程中,失败条件需要区分:文件缺失(模型未下载或编译失败)、摘要不匹配(文件内容变化)、manifest 签名无效(信任源被破坏)。每个条件应触发不同阻断策略而非统一退回默认模型,否则可能被攻击者诱导使用低强度后备方案。
特别是当检测到摘要不匹配时,应立即清除本地缓存并终止进程,防止恶意模型被执行。如果是网络导致的文件缺失,可尝试重试,但必须有次数限制。任何涉及信任根失效的情况,都应视为严重安全事件并上报。
| 失败类型 | 攻击场景 | 阻断动作 | 恢复要求 |
|---|---|---|---|
| manifest 签名无效 | 提供伪造 manifest | 立即阻断,禁止使用任何模型 | 重新获取有效签名 manifest |
| 摘要不匹配,编译新颖 | 非攻击性低,但可能被利用散列选择 | 中断当前模型,上报异常 | 分析是否为已知版本差异 |
| 摘要不匹配,文件替换 | 投毒或权限滥用 | 清除缓存,终止进程 | 重新下载并校验 |
| 文件缺失 | 诱导回退未保护路径 | 不自动回退,报告错误 | 明确用户操作后重试 |
更新框架与回滚防护在移动侧的适用边界
TUF 的设计解决分发网络中混搭、冻结和回滚攻击,但其默认实现面向软件包管理器,未必直接适用移动端模型更新。移动场景下,元数据尺寸、更新频率和网络中断容忍度都有差异,必须裁剪以适应移动网络的波动特性。
在移动应用中,至少需要实施:元数据签名校验、版本单调递增检查、快照过期以及可信时间戳。如果应用仅比对本地缓存的最后版本号,攻击者可通过重放旧版本元数据配合旧模型实现降级,绕过新版本的修复逻辑。
应用长期离线时,版本过期和更新检查会受本地时间及网络状态影响。项目可在客户端保留最低可接受版本,并由服务端在恢复联网后复核;旧模型是降级运行还是拒绝使用,应由业务风险与可用性要求共同决定。
此外,需考虑密钥轮转机制。长期使用同一密钥签名会增加泄露风险,一旦密钥泄露,攻击者可签署任意恶意更新。因此,manifest 应包含密钥有效期,并支持平滑过渡到新密钥,确保旧密钥失效后无法再用于签名。
- 更新元数据是否包含签名且与可信根关联?
- 是否检查元数据内版本号较本地递增?
- 快照元数据是否有过期时间且客户端能验证?
- 是否具备设备时间被篡改时的备份判断?
供应链证明与模型资产可追溯
in-toto 声明格式允许将模型训练数据、依赖版本、编译环境等构建流程信息与制品摘要绑定,帮助识别供应链污染点。然而声明本身是数据对象,其真实性必须由签名和分布式门禁保证,否则只是一份普通的文本记录。
如果客户端只能读取声明字段,却无法核验签名链与发布者身份,这份记录只能作为普通元数据,不能直接参与访问控制。应用启动时应先验证发布者密钥和声明签名,再把其中的模型摘要、版本与本地文件逐项比对。
采用证明机制时,必须将信任边界清晰划定:声明内容说明“某环境产出了某摘要的模型”,但并不能说明“本地编译产物一定来自该环境”。仍需如同本章前述,对本地产物实施独立的摘要比对并与声明中的期望值校验,形成双重确认。
供应链证明的价值在于事后审计和溯源,而非实时的访问控制。在实时场景中,仍应依赖本地的密码学校验。只有当本地校验通过且声明验证无误时,才能确信模型资产的完整性和来源可靠性,缺一不可。
| 证明组件 | 客户端需求 | 篡改检测能力 | 实施限制 |
|---|---|---|---|
| 声明签名 | 持有信任根公钥 | 可检测声明篡改 | 密钥生命周期管理 |
| 物料清单 | 解析并比对制品摘要 | 声明与制品分开可漏报 | 需准确建模产物文件 |
| 构建环境元数据 | 无严格校验手段 | 环境信息仅供参考 | 客户端通常无法核实 |
| 周期性证明更新 | 拉取新声明 | 可防止长期冻结 | 缓存与网络代价 |
- 是否验证了供应链声明的数字签名有效性?
- 声明中的制品摘要是否与本地文件实际摘要一致?
- 是否确认了声明发布者的身份可信且证书未吊销?
- 是否将供应链证明作为辅助手段而非唯一信任依据?
事实依据与适用边界
以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。
| 本文判断 | 事实或工程依据 | 适用限制 |
|---|---|---|
| Core ML 负责模型集成、编译、装载、预测与可选模型加密。 | Apple Core ML | 框架能力不等于业务模型在运行时不可提取。 |
| 动态 Core ML 模型需先下载到本地、编译,再从编译产物初始化。 | Download and compile Core ML models | 下载和编译流程未自动定义来源认证、版本授权或回滚策略。 |
| 模型交付可在包体、下载、精度和本地存储之间做取舍。 | Reduce Core ML app size | 尺寸优化不能证明模型资产的完整性或保密性。 |
| Core ML 支持为编译模型或模型归档生成加密密钥。 | Core ML model encryption key | 模型必须能被框架解密执行,因此加密不消除授权后运行时可见性。 |
| 更新客户端应验证签名、版本、过期时间和一致快照,以识别回滚、冻结和混搭风险。 | The Update Framework specification | TUF 是更新框架,不直接规定移动端模型或 APK 的业务授权策略。 |
| 供应链证明应把产物摘要与有类型的声明负载绑定,避免报告与候选包错配。 | in-toto Attestation Statement v1 | 声明格式不保证声明内容真实,仍需可信签名者和门禁核验。 |
| 内置模型依赖应用包签名,但不能证明模型源自特定发布者。 | Apple Core ML | 应用签名只校验应用整体,不单独对模型资产提供发布者认证。 |
| 编译缓存目录没有自动完整性监控,篡改后可通过定制模型实现代码执行。 | Download and compile Core ML models | 官方编译过程不包含产物完整性校验逻辑,开发者需自行实现核对。 |
工程常见问题
动态下载的 Core ML 模型是否天然安全?
不是。Core ML 下载流程只负责下载和编译,不对模型内容进行签名验证。如果网络层被中间人攻击或服务器被入侵,攻击者可以提供任意模型文件。必须对响应体或编译产物进行独立完整性校验。
模型编译缓存需要受保护吗?
必须受保护。.mlmodelc 是 Core ML 实际执行的版本,攻击者只要替换该目录即可绕过原文件的任何加密或签名。应当核对预期摘要并确保缓存目录权限仅对应用可写。
Core ML 加密模型能阻止逆向吗?
能提高静态提取门槛,但阻止不了运行时逆向。框架在内存中必须解密模型才能计算,因此拥有同样授权或调试权限的攻击者仍可能提取权重和结构。加密必须与其他运行时保护结合。
如何防止模型版本被回滚?
必须在每次更新时校验模型版本号单调递增且结合元数据签名和过期时间。可参考 TUF 的快照机制,应用应硬编码最低可接受版本,并对网络未达的离线场景保持保守策略。
manifest 文件应该签名吗?
必须签名。如果 manifest 仅以纯文本下发,攻击者可同时篡改 manifest 和模型文件使得摘要校验通过。签名后的 manifest 应与信任根关联,并在每次加载前验证签名。
内置模型需要加密吗?
取决于风险。内置模型即使加密,在加载后解密并运行,静态提取难度增加但运行时仍可被分析。若担心越狱提取,可实施加密并配合设备完整性检测,但不保证零可见性。