第 7 章 安全模型

Fayger 的安全模型由两条线索组成:

  • 来源可信:BuF 制品本身可被签名,加载时校验来源与完整性。
  • 运行可控:BuF_Instance 在运行期只能使用被显式授予的能力,未声明的能力一律不可见。

两条线索分别在加载层与适配层落地,并在第 6 章的错误模型与第 4 章的故障隔离中得到加强。

7.1 来源可信:签名与摘要

摘要

每个 Section 在 BuF_Manifest 中都声明了 digest。加载链中的 Verify Digest 阶段对每个 Section 重新计算摘要并比对:

  • 任一段不匹配返回 LDR_DIGEST_MISMATCH
  • 错误 context 包含失败的 section_id

Trailer 中的 manifest_digest 同时校验 Manifest 自身没有被截断或修改。

签名作用范围

签名覆盖的字节范围:

Header || Manifest_minus_signature || Section_Index

设计意图:

  • 即便攻击者在签名后修改 Manifest 的任意字段(除 signature 块本身),签名都会失效。
  • 调整 Section 偏移、长度、摘要也会让签名失效,因为 Section_Index 在签名范围内。
  • Section 段体本身不直接覆盖在签名内,但每段的 digest 都在 Manifest 内,转而间接覆盖。

这种"签 manifest,digest 链 sections"的方式与 OCI Image 签名的取舍相同:签名输入大小可控,验证速度可预测。

支持的签名算法(第一阶段)

  • ed25519
  • ecdsa-p256
  • rsa-pss-sha256

算法标识与公钥引用都在 Manifest 的 signature 块中显式声明,避免基于 ASN.1 / X.509 隐式协商。

签名校验决策表

条件期望结果
强制签名模式开启 ∧ 无签名Err(LDR_SIGNATURE_FAIL),原因 MissingSignature
有签名 ∧ 验证失败Err(LDR_SIGNATURE_FAIL),原因 InvalidSignature
有签名 ∧ 验证通过Ok
强制签名模式关闭 ∧ 无签名Ok(开发便利模式)

7.2 强制签名模式

强制签名模式(enforce signature)是一个加载策略开关,由 LoaderPolicy.require_signature 控制:

struct LoaderPolicy {
  require_signature: bool
  trusted_roots: TrustedRootSet
  allowed_schema_versions: VersionRange
}

行为约束:

  • 开启时:任何 BuF 必须携带签名且签名验证通过,否则拒绝加载。
  • 关闭时:未携带签名的 BuF 仍可加载(开发便利);携带但无效签名的 BuF 仍被拒绝(不允许"假装签了")。

发布场景(生产、可信发布渠道)建议在配置层把 require_signature 默认置为 true

7.3 可信根管理

interface TrustStore {
  current_roots() -> TrustedRootSet
  update(roots: TrustedRootSet)
  enforce_signature() -> bool
}

更新可见性约束:

  • 对一组操作序列 Ops(混合 Update(roots)Load(buf)),每次 Load(buf) 在签名验证时使用的可信根集合必须等于该 Load 之前最后一次 Update 所设置的 roots。
  • 这一约束让"在加载到一半时根集合发生切换"的竞态在语义上不可能发生。

实现策略:

  • 在每次 LoaderPipeline.load 入口处对 TrustStore 拍快照,整次加载使用该快照。
  • 显式拒绝在加载期间共享可变可信根集合。

7.4 运行可控:能力安全模型

适配层采用 capability-based security(与 WASI 一致)。BuF 在 Manifest 中声明能力请求,Platform_Adapter 与宿主策略共同决定授予。

形式化:

granted = requested ∩ available ∩ host_policy
denied  = requested \ granted

启动门控

如果 manifest.required_capabilities \ granted ≠ ∅

  • start() 必须返回错误。
  • 错误 context.missing 等于差集。
  • 实例不进入 Running。

这是"能力不足时禁止启动"的硬约束。

默认拒绝

  • 未声明的能力对 BuF 不可见。
  • Platform_Adapter 不应因"反正能力位看起来对就放过去"而提供未声明的能力。
  • 未识别的 Universal_Instruction 类别一律返回 ADP_UNSUPPORTED_INSTRUCTION,不"沉默放行"。

7.5 能力分类与裁剪要点(按宿主)

宿主类别典型可用能力典型不可用能力说明
桌面io.*net.*ui.*timerandomcryptoproc.*host.*取决于具体平台与权限能力近似全集
服务器io.*net.*timerandomcryptoproc.*ui.* 全部禁用默认无 GUI
浏览器net.fetchnet.websocketui.domtimerandomcrypto 子集proc.*、大部分 io.*强裁剪
In-App由宿主显式注入默认全部禁用最严格,宿主拥有最终决定权

7.6 资源隔离作为安全屏障

资源隔离不仅是稳定性需求,也是安全屏障的一部分:

  • 单个 BuF_Instance 的故障不会扩散到其他实例(见第 4 章的故障隔离性质)。
  • 配额超限会触发暂停,避免一个实例耗尽宿主资源。
  • 不同实例的 RuntimeDataArea 互不可见,避免侧通道信息泄漏在数据层面发生。

7.7 安全 / 错误的交互

签名与能力相关的失败必须以统一错误模型对外返回:

  • 签名失败:LDR_SIGNATURE_FAIL,附 MissingSignature / InvalidSignature 等原因。
  • 能力被拒:ADP_CAPABILITY_DENIED,附被拒能力集合。
  • 启动门控失败:复用 ADP_CAPABILITY_DENIED,context.missing 给出差集。

错误链与第 6 章一致:底层错误置入 cause,上层错误标注自己的 source_layer,调用方可沿链定位。

7.8 安全审计建议

虽然审计本身不在 Fayger 内部范畴,但 Fayger 的事件总线、Lifecycle 事件与 Loader 事件已经为外部审计系统提供了足够锚点:

  • 加载链每一阶段的成功 / 失败事件可作为来源校验的审计点。
  • Lifecycle 事件记录每个实例的启动 / 暂停 / 终止时间线。
  • 能力裁剪结果可作为安全决策的审计点。
  • 签名校验失败、能力被拒、配额超限事件都可被外部 SIEM / 审计 Pipeline 订阅。