插件安全:沙箱、进程隔离与权限控制 / Plugin Security with Sandboxing, Process Isolation, and Permissions
📅 创建时间:2026-07-13 🏷️ 标签:#插件安全 #沙箱 #进程隔离 #seccomp #代码签名 📚 前置知识:runtime dynamic loading, plugin architecture patterns
📋 本章目标
- 全面认识第三方插件的安全风险
- 理解同进程内防护的局限性
- 掌握进程隔离方案:subprocess + IPC
- 了解 Linux 沙箱技术:seccomp、Landlock、cgroups
- 理解代码签名与插件验证机制
第1部分:风险全景
┌─────────────────────────────────────────────────────────────────────────────┐
│ 第三方插件的六大安全风险 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 风险1:恶意代码执行 │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 插件 = 可以执行任意代码 → 理论上可以做任何事 │ │
│ │ dlopen 之后,插件的代码运行在宿主的进程空间内 │ │
│ │ • 读取宿主内存中的敏感数据(密码、密钥、用户数据) │ │
│ │ • 修改宿主的函数指针/虚函数表(hook 宿主逻辑) │ │
│ │ • 调用 system("rm -rf /") │ │
│ │ • 网络连接:向外发送数据 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ 风险2:崩溃传播 │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 插件中的 segfault / null deref / stack overflow │ │
│ │ → 同进程内 → 杀死整个进程(包括宿主和其他插件) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ 风险3:内存泄漏 │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 插件反复分配内存不释放 → 宿主内存逐渐耗尽 → OOM │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ 风险4:资源滥用 │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ CPU 100% 死循环 / 磁盘写满 / 网络带宽占满 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ 风险5:符号劫持 │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 恶意插件导出和宿主内部函数同名的符号 │ │
│ │ 由于动态链接器的符号介入规则 → 恶意的版本可能被优先调用! │ │
│ │ → 这也是为什么插件要用 RTLD_LOCAL 加载 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ 风险6:供应链攻击 │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 插件本身没问题,但依赖了有漏洞的第三方库 │ │
│ │ 或者发布渠道被篡改(下载站点的 .dll 被替换) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘第2部分:防御策略分层
┌─────────────────────────────────────────────────────────────────────────────┐
│ 安全的洋葱模型——多层防御 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ 代码签名+验证│ ← 第4层:来源可信 │
│ ├─────────────┤ │
│ │ 权限声明 │ ← 第3层:最小权限 │
│ ├─────────────┤ │
│ │ 进程隔离 │ ← 第2层:爆炸半径控制 │
│ ├─────────────┤ │
│ │ 代码审查 │ ← 第1层:信任但验证 │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘2.1 同进程内防护(轻量但有限)
class PluginHost {
bool loadPlugin(const std::string& path) {
// 1. 加载前验证签名(如果可以)
if (!verifySignature(path)) return false;
// 2. 用 RTLD_LOCAL 隔离符号
void* handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
// 3. init 时设置超时
auto future = std::async([&] { return plugin->initialize(); });
if (future.wait_for(5s) == std::future_status::timeout) {
// 初始化超时 → 卸载
}
}
// 4. 每个调用都用 try-catch(虽然跨 ABI catch 不保证有效)
int safeCall(IPlugin* plugin, const std::function<int()>& fn) {
// 注意:segfault 无法被 try-catch 捕获!
signal(SIGSEGV, segfault_handler); // 只能捕获,不能恢复
return fn();
}
};同进程防护的本质局限:segfault 和死循环无法被优雅处理——它们直接作用于进程本身。
2.2 进程隔离 —— 真正的安全方案
┌─────────────────────────────────────────────────────────────────────────────┐
│ 进程隔离方案架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 宿主进程 插件进程 │
│ (plugin_host) (plugin_sandbox) │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ │ │ │ │
│ │ PluginManager │ IPC │ PluginRunner │ │
│ │ │◄───────────►│ │ │
│ │ • 发现插件 │ pipe/ │ • dlopen 插件 │ │
│ │ • 管理进程生命周期 │ socket/ │ • 调用插件 │ │
│ │ • 超时检测 │ shm │ • 返回结果 │ │
│ │ │ │ │ │
│ │ plugin_host │ │ plugin_sandbox │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │
│ │ seccomp / Landlock │
│ │ 资源限制 (cgroups) │
│ │
│ 优势: │
│ • 插件崩溃 → 只杀死沙箱进程 → 宿主不受影响 │
│ • 插件 OOM → 只触发沙箱进程的 OOM killer │
│ • 插件 CPU 100% → 只占用沙箱进程 → 宿主仍然响应 │
│ │
│ 代价: │
│ • IPC 序列化/反序列化开销 │
│ • 延迟增加 │
│ • 架构复杂度显著上升 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘第3部分:Linux 沙箱技术
3.1 seccomp —— 限制系统调用
#include <seccomp.h>
// 在沙箱进程中设置 seccomp 过滤器
void setup_seccomp() {
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); // 默认:杀死进程
// 白名单:只允许这些系统调用
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
// 注意:没有 open、socket、fork、exec —— 插件无法访问外部
seccomp_load(ctx);
seccomp_release(ctx);
}3.2 Landlock —— 限制文件系统访问
#include <linux/landlock.h>
void setup_landlock(const char* plugin_dir) {
struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_WRITE_FILE,
};
int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
// 只允许读取插件目录
struct landlock_path_beneath_attr path_attr = {
.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
.parent_fd = open(plugin_dir, O_RDONLY),
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_attr, 0);
landlock_restrict_self(ruleset_fd, 0);
close(ruleset_fd);
}第4部分:代码签名与验证
4.1 加载前验证
// 简单的 GPG 签名验证流程(Linux)
bool verifyPluginSignature(const std::string& plugin_path) {
// 期望存在 plugin.so.sig 签名文件
std::string sig_path = plugin_path + ".sig";
// 用 GPG 验证
std::string cmd = "gpg --verify " + sig_path + " " + plugin_path;
int result = system(cmd.c_str());
return result == 0;
}
// 生产环境中可以用 libgpgme 编程接口替代 system() 调用4.2 权限声明模型
// plugin_manifest.json(随插件分发)
{
"name": "Math Plugin",
"version": "1.0.0",
"signature": "sha256:abc123...",
"permissions": {
"filesystem": {
"read": ["$PLUGIN_DATA/*"],
"write": ["$PLUGIN_DATA/cache/*"]
},
"network": {
"domains": [] // 不需要网络
},
"system": {
"max_memory_mb": 512,
"max_cpu_percent": 50
}
}
}核心总结
┌─────────────────────────────────────────────────────────────────────────────┐
│ 插件安全速查 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 六大风险:恶意代码 / 崩溃传播 / 内存泄漏 / 资源滥用 / 符号劫持 / 供应链攻击 │
│ │
│ 防御分层:代码审查 → 进程隔离 → 权限声明 → 代码签名 │
│ │
│ 进程隔离 = 最有效方案(VS Code 就是这样做的) │
│ │
│ Linux 沙箱三件套: │
│ seccomp —— 限制系统调用 │
│ Landlock —— 限制文件访问 │
│ cgroups —— 限制资源(CPU/内存) │
│ │
│ 代码签名 + manifest 权限声明 = 工业级方案 │
│ │
│ 同进程 = 方便但脆弱(适合信任的插件和内部工具) │
│ 进程隔离 = 安全但复杂(适合不信任的第三方插件) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘章节测试
测试1:信任等级
哪些插件可以安全地运行在同进程中?哪些应该用进程隔离?
测试2:seccomp 的作用
seccomp 是如何保护系统的?它和 Landlock 分别限制什么?
测试3:RTLD_LOCAL 和安全
为什么 RTLD_LOCAL 是安全加载插件的必要条件?
参考答案
测试1答案
答案:同进程中:自己团队开发的、代码审查过的、来源可信的内部插件。进程隔离:从第三方市场下载的、来源不明的、不能审查源码的外部插件。关键判断标准是"你是否完全信任插件的作者"。
测试2答案
答案:seccomp 限制的是系统调用——控制进程能调用哪些内核功能(不能 open 文件、不能 socket、不能 fork 等)。Landlock 限制的是文件系统访问——控制进程能读/写哪些目录和文件。两者互补:seccomp 控制"能力",Landlock 控制"数据访问范围"。
测试3答案
答案:RTLD_LOCAL 确保插件的符号只对它自己可见,不会污染全局符号表。如果不用 RTLD_LOCAL(或用 RTLD_GLOBAL),恶意插件可以导出和宿主内部函数同名的符号。由于 ELF 动态链接器的符号介入(symbol interposition)规则,后加载的符号可能覆盖先加载的——插件导出的"假" validate_token() 可能被宿主的其他模块调用到。
相关笔记
- runtime dynamic loading - dlopen flags 详解
- plugin architecture patterns - 插件发现与生命周期
- hot reload versioning - 热加载
下一步学习
- [ ] 阅读 13 - 工业软件插件系统案例分析
学习状态:🟡 开始学习
工程深化:把本篇知识落到项目里
这一节不是为了凑篇幅,而是把《插件安全:沙箱、进程隔离与权限控制 / Plugin Security with Sandboxing, Process Isolation, and Permissions》从“知道概念”推进到“能在项目里稳定使用”。 阅读时可以把每个知识点都追问成四件事:它保护什么边界,失败时有什么现象,怎样最小复现,怎样写进团队流程。
concept -> boundary -> failure signal -> minimal proof -> project rule工程切片 1:最小可复现样例
**场景。**围绕 信任边界 建一个很小的案例,不要一开始就放进完整业务系统。把问题压缩到一个可以提交给同事的目录,保留源码、构建命令、版本输出和预期现象。
**边界。**先判断这里讨论的是 信任边界、签名 还是 沙箱 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 信任边界 -> 签名 -> 沙箱 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 2:接口边界
**场景。**围绕 签名 建一个很小的案例,不要一开始就放进完整业务系统。写清调用方能依赖什么、不能依赖什么,并把隐式假设转换成命名函数、配置项或测试。
**边界。**先判断这里讨论的是 签名、沙箱 还是 权限 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 签名 -> 沙箱 -> 权限 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 3:失败注入
**场景。**围绕 沙箱 建一个很小的案例,不要一开始就放进完整业务系统。故意制造一个常见错误,让日志、断言或测试先失败,再用修复后的证据说明规则生效。
**边界。**先判断这里讨论的是 沙箱、权限 还是 输入校验 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 沙箱 -> 权限 -> 输入校验 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 4:跨平台差异
**场景。**围绕 权限 建一个很小的案例,不要一开始就放进完整业务系统。至少比较 Windows、Linux 或不同编译器下的一个差异,记录差异属于标准、实现还是平台约定。
**边界。**先判断这里讨论的是 权限、输入校验 还是 审计日志 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 权限 -> 输入校验 -> 审计日志 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 5:性能观察
**场景。**围绕 输入校验 建一个很小的案例,不要一开始就放进完整业务系统。用小规模和压力规模各跑一次,区分算法成本、同步成本、I/O 成本和工具链配置成本。
**边界。**先判断这里讨论的是 输入校验、审计日志 还是 信任边界 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 输入校验 -> 审计日志 -> 信任边界 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 6:生命周期
**场景。**围绕 审计日志 建一个很小的案例,不要一开始就放进完整业务系统。标出资源创建、转移、共享、停止和销毁的顺序,特别关注错误返回和提前退出路径。
**边界。**先判断这里讨论的是 审计日志、信任边界 还是 签名 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 审计日志 -> 信任边界 -> 签名 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 7:诊断证据
**场景。**围绕 信任边界 建一个很小的案例,不要一开始就放进完整业务系统。保留命令、日志、符号、栈、测试输出或截图,让结论可以被另一个环境重新验证。
**边界。**先判断这里讨论的是 信任边界、签名 还是 沙箱 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 信任边界 -> 签名 -> 沙箱 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 8:维护策略
**场景。**围绕 签名 建一个很小的案例,不要一开始就放进完整业务系统。把一次性经验沉淀到 README、CI、脚本、示例工程或检查表里,避免只存在个人记忆中。
**边界。**先判断这里讨论的是 签名、沙箱 还是 权限 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 签名 -> 沙箱 -> 权限 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 9:版本演进
**场景。**围绕 沙箱 建一个很小的案例,不要一开始就放进完整业务系统。为未来变更预留兼容策略:版本号、特性位、弃用窗口、迁移脚本和回滚入口。
**边界。**先判断这里讨论的是 沙箱、权限 还是 输入校验 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 沙箱 -> 权限 -> 输入校验 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 10:最小消费者
**场景。**围绕 权限 建一个很小的案例,不要一开始就放进完整业务系统。从使用者角度写一个小程序或小工程,只依赖公开接口,验证安装包、头文件和运行时行为。
**边界。**先判断这里讨论的是 权限、输入校验 还是 审计日志 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 权限 -> 输入校验 -> 审计日志 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 11:边界复盘
**场景。**围绕 输入校验 建一个很小的案例,不要一开始就放进完整业务系统。用一张表说明问题发生在源码、编译、链接、加载、运行、关闭或发布中的哪一层。
**边界。**先判断这里讨论的是 输入校验、审计日志 还是 信任边界 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 输入校验 -> 审计日志 -> 信任边界 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 12:反例训练
**场景。**围绕 审计日志 建一个很小的案例,不要一开始就放进完整业务系统。写出一个看似能工作但不可维护的方案,再说明它在规模、团队协作或发布时会怎样失败。
**边界。**先判断这里讨论的是 审计日志、信任边界 还是 签名 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 审计日志 -> 信任边界 -> 签名 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 13:最小可复现样例
**场景。**围绕 信任边界 建一个很小的案例,不要一开始就放进完整业务系统。把问题压缩到一个可以提交给同事的目录,保留源码、构建命令、版本输出和预期现象。
**边界。**先判断这里讨论的是 信任边界、签名 还是 沙箱 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 信任边界 -> 签名 -> 沙箱 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 14:接口边界
**场景。**围绕 签名 建一个很小的案例,不要一开始就放进完整业务系统。写清调用方能依赖什么、不能依赖什么,并把隐式假设转换成命名函数、配置项或测试。
**边界。**先判断这里讨论的是 签名、沙箱 还是 权限 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 签名 -> 沙箱 -> 权限 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
本篇收束
掌握《插件安全:沙箱、进程隔离与权限控制 / Plugin Security with Sandboxing, Process Isolation, and Permissions》的标志,不是记住所有名词,而是能把 信任边界、签名、沙箱、权限、输入校验、审计日志 放进一条可验证的工程链路。 先用最小样例建立判断,再用测试和脚本固定判断,最后把失败证据留给未来的自己和团队。