第 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 签名的取舍相同:签名输入大小可控,验证速度可预测。
支持的签名算法(第一阶段)
ed25519ecdsa-p256rsa-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.*、time、random、crypto、proc.*、host.* | 取决于具体平台与权限 | 能力近似全集 |
| 服务器 | io.*、net.*、time、random、crypto、proc.* | ui.* 全部禁用 | 默认无 GUI |
| 浏览器 | net.fetch、net.websocket、ui.dom、time、random、crypto 子集 | 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 订阅。
