1. SecurityClaw 学习笔记/

16 安全护栏与权限:Agent 能规划不等于能执行

结论先说:安全护栏不是 prompt 附件,而是运行时控制系统 #

一个 Agent 系统里,最危险的误解就是:

只要在 prompt 里告诉模型“不要做危险操作”,系统就安全了。

这当然不够。

真正的安全护栏必须在运行时落地,至少包括:

  • 权限
  • 人工确认
  • 幂等性
  • 预算
  • 注入防护

一、为什么“能规划”不等于“能执行” #

LLM 可以很擅长做这些事:

  • 理解意图
  • 提出动作
  • 组合多步计划

但它不该拥有这些权力:

  • 决定自己有没有权限
  • 决定高风险动作是否可以直接落地
  • 决定同一操作失败后能否无限重试
  • 决定哪段用户输入可以直接进入系统调用

所以最核心的原则是:

规划权可以给模型,执行权必须收回到系统。


二、权限护栏:动作白名单而不是善意假设 #

class PermissionRule(BaseModel):
    action: Literal["read", "write", "external", "modify_file"]
    entities: list[str]


def validate_permissions(plan, manifest):
    for step in plan.steps:
        if step.action not in manifest.allowed_actions:
            return False
        if step.entity not in manifest.allowed_entities:
            return False
    return True

这一步不是“提醒”,而是拒绝条件。

为什么这层必须在运行时 #

因为 prompt 规则无法保证:

  • 模型不幻觉
  • 工具名不漂移
  • 用户输入不诱导模型越权

只有运行时白名单,才是硬边界。


三、人工确认:高风险动作为什么必须暂停 #

有些动作根本不该自动通过:

  • 删除数据
  • 改权限
  • 发外部邮件
  • 写敏感路径文件
  • 对外联网请求高风险地址
if manifest.risk_level == "high":
    request = HumanConfirmRequest(plan=step, timeout=30)
    approved = wait_for_confirmation(request)
    if not approved:
        return Denied("confirmation timeout")

这一步的重要性在于:

  • 把组织责任留在人
  • 把不可逆动作从模型自动化里摘出来
  • 给系统留一个最后的刹车点

四、幂等性:恢复不能放大事故 #

恢复机制如果没有幂等护栏,会非常危险。

if execution_records.exists(idempotency_key):
    return execution_records.get(idempotency_key)

这层防的是什么 #

  • 重试导致重复写库
  • 重试导致重复发信
  • 重试导致重复外呼
  • 同一失败路径被反复放大

所以幂等性不是优化,而是安全护栏的一部分。


五、预算护栏:安全不只是权限,也是成本边界 #

if token_budget.remaining <= 0:
    return Stop("budget exhausted")

if tool_cost[tool_name] > remaining_cost:
    return Denied("tool budget exceeded")

很多人把预算理解成成本控制,其实它同时是防滥用边界:

  • 防无限循环
  • 防外部 API 被打爆
  • 防单个用户问题拖垮系统资源

从系统安全角度看,它和权限一样重要。


六、注入防护:为什么不能让用户输入直接影响执行层 #

在 Agent 场景里,注入不只是 prompt injection,也包括:

  • 用户输入污染工具参数
  • 外部文档内容反向影响控制流
  • 检索结果把系统带偏

所以防护至少应该包括:

6.1 参数白名单 #

只允许结构化参数进入工具

6.2 用户输入与系统约束分离 #

用户能影响目标,不能影响执行边界

6.3 检索证据不直接等同于命令 #

RAG 结果是证据,不是执行指令

这也是为什么“自然语言拼系统命令”是危险设计。


七、为什么安全护栏不能全放在 manifest 里 #

manifest 可以声明:

  • allowed actions
  • allowed fields
  • risk level
  • idempotent

但它不能成为最终安全边界,因为:

  • manifest 本身也可能出错
  • manifest 只是框架层声明
  • 真正执行动作的权限最终还在基础设施层

所以更准确的分层是:

manifest: 声明策略
runtime: 执行策略
infra: 最终兜底

少任何一层,护栏都会变脆。


八、这一篇真正要记住的框架 #

安全护栏可以压成这五个问题:

这个动作允许吗?
这个动作需要人工确认吗?
这个动作失败后能安全重试吗?
这个动作值得继续消耗预算吗?
用户输入会不会污染执行层?

如果系统不能系统性回答这五个问题,那它只是“带工具的 LLM”,还不是可上线 Agent。


九、验证命令 #

pytest tests/ -k "permission or guardrail or idempotent or confirm or budget" -v