智能体面试准备(八十):智能体凭证治理与最小权限访问工程------密钥托管、临时凭证与全链路审计
引言
本文是系列第 80 篇。前面讲过工具沙箱、人在回路、多租户权限,这一篇聚焦一个更细但后果极重的问题:Agent 调用外部系统时,凭证(Credential)怎么管。
这件事一旦做错,后果是灾难性的:把数据库密码写进 prompt、把长期有效的 API Key 硬编码在镜像里、Agent 被提示词注入后拿着高权限 token 删库。而面试官考察"Agent 安全"时,凭证治理是绕不开的一环------因为它最能区分"知道要加锁"和"真的做过权限体系"。
本篇讲清四件事:凭证不能出现在模型可见的任何地方、要用短期凭证、要按任务最小授权、要能追溯每一次使用。
凭证治理总览
==================================================
用户 / 上游系统
│ 1. 身份认证(OIDC/OAuth)
▼
┌─────────────────────┐
│ 凭证颁发服务 STS │
│ 签发短期、受限凭证 │
└──────────┬──────────┘
│ 2. 短期凭证(分钟级、范围受限)
▼
┌─────────────────────┐
│ Agent 运行时 │
│ ┌───────────────┐ │
│ │ 凭证代理 Proxy │ │ <- 模型永远拿不到明文
│ └───────┬───────┘ │
└──────────┼──────────┘
│ 3. 代理注入到真实调用
▼
外部系统(DB / SaaS / 内部 API)
│
▼ 4. 审计与回溯
一、铁律:凭证绝不进模型上下文
先看反面清单,这些都是真实事故:
| 反模式 | 后果 |
|---|---|
| API Key 写进 system prompt | 模型可能原样输出(注入/追问即泄露) |
| 密钥硬编码在镜像/环境变量 | 镜像泄漏即全量泄露,轮换要重新发版 |
| 用同一个高权限账号跑所有任务 | 一次越权影响全部数据 |
| 凭证长期有效(不过期) | 泄露后窗口无限大 |
| 把数据库 DSN(含密码)传给 LLM 让它"自己连" | 完全失控 |
正确做法是凭证代理(Credential Proxy):模型只发出"我要调用 X 工具"的意图,真实凭证由旁路代理注入,模型全程接触不到明文。
# 反例:凭证进入 prompt
bad_prompt = f"用这个 key 调接口:api_key={SECRET}" # 严禁
# 正例:Agent 只声明意图,凭证由代理注入
def invoke_tool(tool_name, args, ctx):
cred = sts.issue( # 向凭证服务申请短期、限范围凭证
tool=tool_name,
tenant=ctx.tenant,
ttl_seconds=300,
scope=f"{tool_name}:read", # 最小权限
reason=ctx.task_id,
)
headers = {"Authorization": f"Bearer {cred.token}"}
return http_call(TOOLS[tool_name].endpoint, args, headers=headers)
二、短期凭证与最小权限
凭证设计三原则:短期、最小、可撤销。
| 原则 | 做法 | 参数示例 |
|---|---|---|
| 短期 | TTL 分钟级,用完即废 | ttl=300s |
| 最小权限 | 按工具+操作+资源范围授权 | crm:order:read 而非 crm:* |
| 可撤销 | 支持按 task_id 即时吊销 | revoke(task_id) |
| 绑定上下文 | 凭证绑定租户/会话/任务 | 换任务即失效 |
class ScopedCredential:
def __init__(self, token, scope, expires_at, task_id):
self.token, self.scope = token, scope
self.expires_at, self.task_id = expires_at, task_id
def valid(self, required_scope, task_id):
return (time.time() < self.expires_at
and task_id == self.task_id
and scope_allows(self.scope, required_scope))
def scope_allows(granted, required):
"""支持前缀通配的最小权限判定"""
g, r = granted.split(":"), required.split(":")
return len(g) == len(r) and all(a == b or a == "*" for a, b in zip(g, r))
三、按任务授权与人工升级
不同风险等级的任务,凭证获取方式不同:
| 风险等级 | 示例 | 授权方式 |
|---|---|---|
| 只读低风险 | 查询天气、检索文档 | 自动颁发,无需审批 |
| 有副作用 | 发消息、写工单 | 自动颁发 + 事后通知 |
| 高风险 | 转账、删数据、发外部邮件 | 人工审批后颁发,或人在回路确认 |
| 越权请求 | 超出该 Agent 权限范围 | 直接拒绝并告警 |
实现上给每个工具标注风险等级(注册在工具注册中心,见上篇 B79),凭证服务根据等级决定"自动发 / 需审批 / 拒绝"。风险分级 + 自动颁发 + 高风险人工升级 是标准三段式。
RISK = {"read_only": "auto", "side_effect": "auto+notify",
"high_risk": "require_approval"}
def issue_for(tool, ctx):
level = TOOLS[tool].risk
policy = RISK[level]
if policy == "require_approval":
if not approvals.granted(ctx.task_id, tool):
raise PermissionError(f"{tool} 需人工审批")
return sts.issue(tool=tool, tenant=ctx.tenant,
ttl_seconds=300 if level != "high_risk" else 600,
scope=TOOLS[tool].required_scope, reason=ctx.task_id)
四、防注入:凭证在攻防中的位置
提示词注入的真实危害不是"模型说错话",而是攻击者借模型之手调用高权限工具。凭证治理正是对抗链的关键一环:
防线叠加(缺一不可)
1. 凭证不进上下文 -> 注入者无法直接套出密钥
2. 最小权限 scope -> 即使被诱导调用,影响范围受限
3. 高风险需审批 -> 关键动作有人工闸门
4. 短期 TTL -> 泄露窗口极小
5. 全链路审计 -> 事后可追溯、可告警
注意:不要把"模型判断安全"当作唯一闸门。模型的判断可被诱导,真正的闸门必须是策略引擎(代码级、确定性),模型只能提供建议。
五、审计与轮换
| 能力 | 说明 |
|---|---|
| 审计日志 | 每次凭证颁发与使用记录:谁、何时、工具、参数摘要、结果 |
| 异常检测 | 频率异常、非常规时段、跨租户访问、权限提升尝试 |
| 密钥轮换 | 定期自动轮换;泄露时一键吊销 |
| 最小可见 | 日志中脱敏,绝不记录明文凭证 |
def audit_log(tool, args, ctx, result):
log.info({
"ts": time.time(),
"tenant": ctx.tenant,
"task_id": ctx.task_id,
"tool": tool,
"args_digest": hashlib.sha256(
json.dumps(args, sort_keys=True).encode()).hexdigest()[:16],
"status": "ok" if result.ok else "fail",
# 注意:不记录明文参数与凭证
})
六、落地检查清单
- 凭证是否 100% 不进入模型上下文(prompt / 工具返回值 / 日志)?
- 是否全部使用短期凭证(TTL ≤ 分钟级)?
- 是否按"工具+操作+资源"三维最小授权?
- 高风险工具是否有人工审批闸门,且闸门在策略层而非模型层?
- 是否能按 task_id 即时吊销?
- 是否所有调用都有审计,且日志脱敏?
- 密钥是否支持自动轮换与一键吊销?
面试速答
问:Agent 调用外部 API 的密钥怎么管?
答:凭证绝不进模型上下文。用凭证代理:模型只声明调用意图,真实凭证由旁路服务按"短期 TTL + 最小 scope + 绑定 task_id"签发并注入,模型全程接触不到明文。
问:短期凭证的具体参数怎么设?
答:TTL 分钟级(如 300s),scope 按工具+操作+资源三维最小授权(如 crm:order:read 而非 crm:*),绑定 task_id 与租户,支持按 task_id 即时吊销。
问:提示词注入怎么防?
答:五层叠加------凭证不进上下文、最小权限 scope、高风险人工审批、短期 TTL、全链路审计。关键是安全闸门必须在策略层(代码确定性),不能依赖模型判断。
问:高风险操作怎么控制?
答:工具在注册中心标注风险等级,凭证服务按等级决定自动颁发/需审批/拒绝。高风险(转账、删数据、外发邮件)走人工审批或人在回路确认,审批通过才发凭证。
高频追问清单
- 凭证代理挂了或被绕过,怎么保证 Agent 拿不到明文?
- 多租户场景下凭证怎么隔离?跨租户访问怎么检测?
- 短期凭证 TTL 设太短导致频繁申请,性能和可用性怎么权衡?
- 工具返回值里意外带了密钥(如接口返回 token),怎么清洗?
- Agent 需要代表用户操作(on-behalf-of),OAuth 委托授权怎么设计?
- 高风险审批流超时未响应,任务应该阻塞、降级还是中止?
- 凭证审计日志本身怎么防篡改?
- 密钥自动轮换时,在途请求怎么处理才不中断?
- 如何检测"凭证被异常使用"(频率、地理、时段异常)?
- 本地/私有化部署没有云厂商 STS,怎么自建短期凭证服务?