纵深防御
Triggerfish 将安全实现为 13 个独立、重叠的层。没有单一层本身是充分的。它们共同形成一道优雅降级的防御——即使某一层被攻破,其余层仍然继续保护系统。
安全 纵深防御意味着任何单一层的漏洞都不会危及系统。绕过渠道认证的攻击者仍然面临会话 taint 跟踪、策略 hook 和审计日志。被提示注入的 LLM 仍然无法影响其下方的确定性策略层。 :::
13 层
第 1 层:渠道认证
防范: 冒充、未授权访问、身份混淆。
身份由会话建立时的代码确定,而非 LLM 解释消息内容。在 LLM 看到任何消息之前,渠道适配器会为其添加不可变标签:
{ source: "owner" } -- 已验证的渠道身份与注册所有者匹配
{ source: "external" } -- 其他任何人;仅作为输入,不被视为命令认证方法因渠道而异:
| 渠道 | 方法 | 验证方式 |
|---|---|---|
| Telegram / WhatsApp | 配对码 | 一次性代码,5 分钟过期,从用户账户发送 |
| Slack / Discord / Teams | OAuth | 平台 OAuth 授权流程,返回已验证的用户 ID |
| CLI | 本地进程 | 运行在用户的机器上,由操作系统认证 |
| WebChat | 无(公开) | 所有访客为 EXTERNAL,永远不是 owner |
| 电子邮件 | 域名匹配 | 发送者域名与配置的内部域名进行比较 |
LLM 永远不会决定谁是所有者。来自未验证发送者的消息"我是所有者"会被标记为 { source: "external" },且无法触发所有者级别的命令。此决策在代码中做出,在 LLM 处理消息之前。 :::
第 2 层:权限感知数据访问
防范: 过度授权的数据访问、通过系统凭证进行权限提升。
Triggerfish 使用用户的委托 OAuth 令牌——而非系统服务账户——来查询外部系统。源系统执行其自己的权限模型:
Plugin SDK 在 API 级别执行此规则:
| SDK 方法 | 行为 |
|---|---|
sdk.get_user_credential(integration) | 返回用户的委托 OAuth 令牌 |
sdk.query_as_user(integration, query) | 使用用户的权限执行 |
sdk.get_system_credential(name) | 已阻止 —— 抛出 PermissionError |
第 3 层:会话 Taint 跟踪
防范: 通过上下文污染导致的数据泄露、分类数据到达更低分类渠道。
每个会话独立跟踪一个 taint 级别,反映会话期间访问的最高数据分类。Taint 遵循三个不变量:
- 按对话 —— 每个会话有自己的 taint
- 仅升级 —— taint 增加,永不减少
- 完全重置清除一切 —— taint 和历史一起清除
当策略引擎评估输出时,它将会话的 taint 与目标渠道的有效分类进行比较。如果 taint 超过目标,输出被阻止。
第 4 层:数据溯源
防范: 不可追踪的数据流、无法审计数据去向、合规缺口。
每个数据元素从源到目标都携带出处元数据:
- 来源:哪个集成、记录和用户访问产生了此数据
- 分类:分配了什么级别以及原因
- 转换:LLM 如何修改、摘要或组合数据
- 目标:哪个会话和渠道接收了输出
溯源支持前向追踪("这条 Salesforce 记录去了哪里?")、后向追踪("哪些来源贡献了此输出?")和完整的合规导出。
第 5 层:策略执行 Hook
防范: 提示注入攻击、LLM 驱动的安全绕过、不受控的工具执行。
八个确定性 hook 在数据流的关键点拦截每个操作:
| Hook | 拦截内容 |
|---|---|
PRE_CONTEXT_INJECTION | 进入上下文窗口的外部输入 |
PRE_TOOL_CALL | LLM 请求工具执行 |
POST_TOOL_RESPONSE | 工具执行返回的数据 |
PRE_OUTPUT | 即将离开系统的响应 |
SECRET_ACCESS | 凭证访问请求 |
SESSION_RESET | Taint 重置请求 |
AGENT_INVOCATION | 智能体间调用 |
MCP_TOOL_CALL | MCP 服务器工具调用 |
Hook 是纯代码:确定性、同步、有日志记录且不可伪造。LLM 不能绕过它们,因为从 LLM 输出到 hook 配置没有通路。Hook 层不解析 LLM 输出以寻找命令。
第 6 层:MCP Gateway
防范: 不受控的外部工具访问、通过 MCP 服务器进入的未分类数据、模式违规。
所有 MCP 服务器默认为 UNTRUSTED,在管理员或用户对其进行分类之前不能被调用。Gateway 执行:
- 服务器认证和分类状态
- 工具级权限(即使服务器被允许,单个工具也可以被阻止)
- 请求/响应模式验证
- 所有 MCP 响应的 taint 跟踪
- 参数中的注入模式扫描
第 7 层:Plugin 沙箱
防范: 恶意或有缺陷的 plugin 代码、数据外泄、未授权系统访问。
Plugin 在双重沙箱中运行:
Plugin 不能:
- 访问未声明的网络端点
- 发出没有分类标签的数据
- 在不触发 taint 传播的情况下读取数据
- 在 Triggerfish 之外持久化数据
- 使用系统凭证(仅用户的委托凭证)
- 通过侧信道外泄数据(资源限制,无原始套接字)
Plugin 沙箱不同于智能体执行环境。Plugin 是系统_保护自身免受_的不受信代码。执行环境是智能体被允许_构建_的工作区——具有策略管控的访问,而非沙箱隔离。 :::
第 8 层:密钥隔离
防范: 凭证被盗、配置文件中的密钥、明文凭证存储。
凭证存储在操作系统钥匙串(个人版)或保管库集成(企业版)中。它们永远不会出现在:
- 配置文件
StorageProvider的值- 日志条目
- LLM 上下文(凭证在 HTTP 层注入,位于 LLM 之下)
SECRET_ACCESS hook 记录每次凭证访问,包括请求的 plugin、凭证范围和决策。
第 9 层:文件系统工具沙箱
防范: 路径遍历攻击、未授权文件访问、通过直接文件系统操作绕过分类。
所有文件系统工具操作(读取、写入、编辑、列出、搜索)在沙箱化的 Deno Worker 中运行,其操作系统级权限限定在会话 taint 对应的工作区子目录。沙箱执行三个边界:
- 路径限制 —— 每个路径被解析为绝对路径,并使用分隔符感知匹配检查限制根目录。试图逃出工作区的遍历尝试(
../)在任何 I/O 发生之前被拒绝 - 路径分类 —— 每个文件系统路径通过固定解析链进行分类:硬编码保护路径(RESTRICTED)、工作区分类目录、配置路径映射、然后是默认分类。智能体不能访问高于其会话 taint 的路径
- Taint 范围的权限 —— 沙箱 Worker 的 Deno 权限设置为与会话当前 taint 级别匹配的工作区子目录。当 taint 升级时,Worker 以扩展的权限重新生成。权限只能扩大,在会话内永远不能缩小
- 写保护 —— 关键文件(
TRIGGER.md、triggerfish.yaml、SPINE.md)在工具层被写保护,与沙箱权限无关。这些文件只能通过执行其自身分类规则的专用管理工具修改
第 10 层:智能体身份
防范: 通过智能体链进行权限提升、通过委托进行数据洗白。
当智能体调用其他智能体时,加密委托链防止权限提升:
- 每个智能体都有一个证书,指定其能力和分类上限
- 被调用者继承
max(own taint, caller taint)—— taint 只能通过链增加 - taint 超过被调用者上限的调用者会被阻止
- 循环调用被检测并拒绝
- 委托深度被限制和执行
第 11 层:审计日志
防范: 不可检测的违规、合规失败、无法调查事件。
每个与安全相关的决策都记录有完整上下文:
json
{
"timestamp": "2025-01-29T10:23:45Z",
"user_id": "user_123",
"session_id": "sess_456",
"action": "slack.postMessage",
"target_channel": "external_webhook",
"session_taint": "CONFIDENTIAL",
"target_classification": "PUBLIC",
"decision": "DENIED",
"reason": "classification_violation",
"hook": "PRE_OUTPUT",
"policy_rules_evaluated": ["rule_001", "rule_002"],
"lineage_ids": ["lin_789", "lin_790"]
}记录的内容:
- 所有操作请求(允许和拒绝)
- 分类决策
- 会话 taint 变更
- 渠道认证事件
- 策略规则评估
- 溯源记录创建和更新
- MCP Gateway 决策
- 智能体间调用
审计日志不能被禁用。这是策略层级中的一条固定规则。即使组织管理员也不能关闭对自己操作的日志记录。企业部署可以选择启用完整内容日志(包括被阻止的消息内容)以满足取证需求。 :::
第 12 层:SSRF 防护
防范: 服务器端请求伪造、内部网络侦察、云元数据外泄。
所有出站 HTTP 请求(来自 web_fetch、browser.navigate 和 plugin 网络访问)首先解析 DNS,然后将解析的 IP 与硬编码的私有和保留范围拒绝列表进行比较。这防止攻击者通过精心构造的 URL 诱骗智能体访问内部服务。
- 私有范围(
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)始终被阻止 - 链路本地地址(
169.254.0.0/16)和云元数据端点被阻止 - 回环地址(
127.0.0.0/8)被阻止 - 拒绝列表是硬编码的,不可配置——没有管理员覆盖
- DNS 解析在请求之前进行,防止 DNS 重绑定攻击
第 13 层:内存分类门控
防范: 通过内存进行的跨会话数据泄露、通过内存写入进行的分类降级、对分类记忆的未授权访问。
跨会话内存系统在写入和读取时都执行分类:
- 写入:内存条目被强制设为当前会话的 taint 级别。LLM 不能为存储的记忆选择更低的分类。
- 读取:内存查询通过
canFlowTo过滤——会话只能读取等于或低于其当前 taint 级别的记忆。
这防止智能体将 CONFIDENTIAL 数据作为 PUBLIC 存储在内存中,然后在更低 taint 的会话中检索它以绕过禁止降级写入规则。
信任层级
信任模型定义谁对什么有权限。更高层级不能绕过更低层级的安全规则,但可以配置这些规则中的可调参数。
个人版: 用户就是组织管理员。完全主权。没有 Triggerfish 可见性。供应商默认对用户数据零访问,只能通过用户发出的明确、有时限、有日志记录的授权获得访问。 :::
各层如何协同工作
考虑一个提示注入攻击,其中恶意消息试图外泄数据:
| 步骤 | 层 | 操作 |
|---|---|---|
| 1 | 渠道认证 | 消息标记为 { source: "external" } —— 非所有者 |
| 2 | PRE_CONTEXT_INJECTION | 输入被扫描注入模式,进行分类 |
| 3 | 会话 taint | 会话 taint 不变(未访问分类数据) |
| 4 | LLM 处理消息 | LLM 可能被操纵请求工具调用 |
| 5 | PRE_TOOL_CALL | 根据外部来源规则检查工具权限 |
| 6 | POST_TOOL_RESPONSE | 返回的数据被分类,taint 更新 |
| 7 | PRE_OUTPUT | 输出分类与目标进行检查 |
| 8 | 审计日志 | 记录整个序列以供审查 |
即使 LLM 在第 4 步被完全攻破并请求数据外泄工具调用,其余层(权限检查、taint 跟踪、输出分类、审计日志)仍继续执行策略。没有单点故障能危及系统。
