【大模型安全实战】Agent 数据防泄露:从“出口拦截”到“源头加固”的三层防线(第3期)
【大模型安全实战】Agent 数据防泄露:从“出口拦截”到“源头加固”的三层防线(第3期)
专栏:智能体攻防卷·进阶
期数:第 3 期
作者:Valhalla Matrix治理实验室
文章类型:AI Agent 安全工程实践
论文线索:Data Leakage Prevention in Agentic Applications via Preemptive Hardening,arXiv:2607.18847
说明:本文基于给定论文线索和 Agent 安全工程实践进行技术梳理,不替代论文原文、产品安全评估或合规审计。
摘要
在传统应用中,数据防泄露往往依赖出口检测:当系统准备将数据发送给外部服务、写入日志或返回用户时,再进行脱敏、拦截和审计。
这种方式对普通应用尚且存在遗漏,对能够自主读取文件、调用工具、访问数据库并生成自然语言结果的 AI Agent 来说,风险会进一步放大。
问题在于:当敏感数据已经进入 Agent 上下文后,系统通常很难准确判断它会被怎样使用、组合和传播。此时再依靠出口拦截,可能已经错过了最容易控制的阶段。
更稳妥的方案是将防线前移:
数据进入 Agent 之前
→ 确认身份与任务
→ 判断所需字段
→ 进行最小化投影
→ 注入敏感度标签
→ 经过工具和出口策略
→ 记录完整审计轨迹
本文将围绕三个核心机制展开:
- 字段级最小可见;
- 访问前授权核对;
- 数据流动全程标记。
一、为什么 Agent 的数据泄露更难控制?
传统应用的数据流通常比较固定:
用户请求
→ 后端服务
→ 数据库
→ 业务处理
→ 返回结果
而 Agent 的数据流往往是动态的:
用户请求
→ 模型理解任务
→ 选择工具
→ 读取文件或数据库
→ 继续推理
→ 再次调用工具
→ 调用外部服务
→ 生成最终结果
在这个过程中,Agent 可能同时处理:
- 用户输入;
- 系统提示;
- Skill 或插件内容;
- 数据库记录;
- 本地文件;
- Shell 命令输出;
- Web 页面内容;
- 其他 Agent 的消息;
- 历史会话和记忆。
这些内容会被拼接到上下文中,并在后续步骤中持续流动。
因此,数据泄露风险不仅来自“最终返回了什么”,还来自:
- Agent 是否读取了不必要的字段;
- 敏感信息是否进入模型上下文;
- 工具返回值是否包含机密;
- 日志是否记录了原始内容;
- 子代理是否继承了过高权限;
- 外部服务是否接收了完整上下文;
- 模型是否将多个低敏字段重新组合成高敏信息。
可以将 Agent 的数据暴露面表示为:
[
E = D \times T \times C \times O
]
其中:
- (D):Agent 可访问的数据量;
- (T):可调用工具数量;
- (C):上下文传播范围;
- (O):外部出口数量。
只要这四个因素同时扩大,泄露风险就会快速增加。
二、什么是预置加固?
预置加固的核心思想不是“取消出口检查”,而是:
在数据进入 Agent、工具或模型上下文之前,先完成身份、任务、字段和权限判断。
传统模式更接近:
读取完整数据
→ 交给 Agent 处理
→ 最后检查输出
预置加固模式则是:
识别任务与身份
→ 计算最小数据需求
→ 生成受限数据视图
→ 标记数据敏感度
→ 执行 Agent 任务
→ 对工具和输出继续检查
两者不是二选一。
更合理的安全架构是:
源头最小化
+ 访问前授权
+ 处理中标记
+ 出口检测
+ 审计与告警
其中,源头最小化负责降低暴露面;访问前授权负责控制能力;数据标记负责追踪传播;出口检测负责处理最后一道边界。
三、第一层防线:字段级最小可见
3.1 不要把整张表交给 Agent
假设系统中存在客户表:
customer_id
name
email
phone
address
government_id
payment_token
internal_risk_score
客服 Agent 只需要确认客户地址时,合理的数据视图可能是:
customer_id
name
address
而不是把完整客户记录交给模型,再要求它“不要使用敏感字段”。
原因很简单:
一旦敏感字段进入模型上下文,系统就很难保证它不会出现在后续推理、工具调用、日志或错误信息中。
可以将数据最小化表示为:
[
D_{\text{agent}} = D_{\text{source}} \cap D_{\text{task}} \cap D_{\text{policy}}
]
其中:
- (D_{\text{source}}):数据源实际拥有的字段;
- (D_{\text{task}}):当前任务所需字段;
- (D_{\text{policy}}):当前身份允许访问的字段。
只有三者的交集,才应进入 Agent 上下文。
3.2 用数据访问视图实现最小化
以数据库为例,可以为不同 Agent 建立受限视图:
CREATE VIEW customer_support_view AS
SELECT
customer_id,
name,
address,
support_status
FROM customers;
对于更复杂的系统,可以根据身份和任务动态生成字段投影:
type AccessContext = {
actorId: string;
agentId: string;
purpose: string;
tenantId: string;
};
const fieldPolicy = {
customer_support: [
"customer_id",
"name",
"address",
"support_status",
],
billing_review: [
"customer_id",
"name",
"payment_status",
],
};
function projectRecord(
record: Record<string, unknown>,
purpose: keyof typeof fieldPolicy,
) {
const allowedFields = fieldPolicy[purpose];
return Object.fromEntries(
allowedFields
.filter((field) => field in record)
.map((field) => [field, record[field]]),
);
}
需要注意:代码中的字段白名单只是示例。生产环境还应增加:
- 租户隔离;
- 行级权限;
- 数据分类;
- 访问目的;
- 用户授权;
- 字段脱敏;
- 审计记录;
- 策略版本。
3.3 最小可见不等于简单删字段
字段级最小化需要考虑业务可用性。
例如:
- 邮箱可能需要部分脱敏,而不是完全删除;
- 手机号可能只保留后四位;
- 金额可能保留区间而不是精确值;
- 身份标识可以使用不可逆别名;
- 地址可能只保留城市和区域;
- 时间信息可能降级到日期而不是精确时间。
常见处理方式包括:
完整值:13812345678
脱敏值:138****5678
完整邮箱:user@example.com
脱敏邮箱:u***@example.com
完整金额:128,673.25
区间值:10 万~20 万
完整身份标识:原始 ID
替代值:稳定的不可逆内部标识
关键原则是:
只有在业务确实需要精确值时,才提供精确值。
四、第二层防线:访问前授权核对
字段最小化解决的是“Agent 能看到什么”,但还需要解决:
Agent 为什么可以看到这些数据?
4.1 权限不应只绑定到用户
传统权限模型通常只关注:
用户是谁?
Agent 场景还需要判断:
哪个 Agent?
为了什么任务?
调用哪个工具?
访问哪个资源?
需要什么操作?
访问多长时间?
是否需要人工确认?
可以将一次访问请求抽象为:
[
A = f(I, G, R, O, T, P)
]
其中:
- (I):身份 Identity;
- (G):任务目标 Goal;
- (R):资源 Resource;
- (O):操作 Operation;
- (T):时间和会话上下文;
- (P):当前策略 Policy。
只有当这些条件共同满足时,访问才应被允许。
4.2 工具调用前进行策略检查
一个工具调用请求可以表示为:
{
"agent_id": "support-agent",
"session_id": "session-123",
"tool": "customer.lookup",
"resource": "customer:10086",
"operation": "read",
"purpose": "resolve_delivery_issue",
"requested_fields": [
"name",
"address",
"support_status"
]
}
策略引擎需要判断:
- 当前 Agent 是否可以调用
customer.lookup; - 当前用户是否拥有相应业务权限;
- 当前租户是否匹配;
- 查询目的是否允许;
- 请求字段是否超过最小范围;
- 是否需要用户确认;
- 是否触发高敏数据规则;
- 是否存在异常频率或异常组合。
策略结果不应只有 allow 和 deny,还可以包含:
{
"decision": "allow_with_transform",
"allowed_fields": [
"name",
"address",
"support_status"
],
"transform": "mask_contact",
"audit_level": "high",
"expires_in_seconds": 60
}
这比让 Agent 先获取完整数据、之后再过滤更加可靠。
4.3 权限应当短时有效
对于敏感工具,不建议在 Agent 会话启动时一次性授予长期权限。
更稳妥的模式是:
工具调用请求
→ 权限评估
→ 创建短时执行上下文
→ 获取最小范围数据
→ 返回脱敏结果
→ 回收临时权限
短时权限可以降低:
- Prompt 注入造成的持续影响;
- Skill 被污染后的访问范围;
- 子代理权限扩散;
- 凭证长期暴露;
- 任务完成后权限残留。
五、第三层防线:数据流动全程标记
源头最小化和访问前授权仍然可能被绕过或配置错误,因此需要知道数据在系统中如何流动。
5.1 为数据增加敏感度标签
可以为数据对象附加元数据:
type Sensitivity =
| "public"
| "internal"
| "confidential"
| "restricted";
type TaggedValue<T> = {
value: T;
sensitivity: Sensitivity;
source: string;
purpose: string;
tenantId: string;
expiresAt?: string;
};
示例:
{
"value": "138****5678",
"sensitivity": "confidential",
"source": "customer_support_view",
"purpose": "resolve_delivery_issue",
"tenantId": "tenant-a",
"expiresAt": "2026-08-17T13:00:00Z"
}
标签至少应支持:
- 数据敏感等级;
- 数据来源;
- 所属租户;
- 访问目的;
- 生命周期;
- 是否允许进入模型;
- 是否允许写入日志;
- 是否允许发送到外部服务。
5.2 标签不能只存在于内存对象中
如果标签只存在于某个 TypeScript 对象里,数据经过以下操作后可能丢失:
- JSON 序列化;
- 数据库写入;
- 工具调用;
- 子代理传递;
- 消息队列;
- 日志记录;
- 外部 API 调用。
因此,标签传播需要明确规则:
数据读取
→ 标签生成
→ 上下文拼接
→ 工具传递
→ 子代理传递
→ 持久化
→ 输出审查
可以使用“最高敏感级别继承”作为基础策略:
[
L_{\text{output}}
\max(L_1, L_2, \dots, L_n)
]
例如:
public + internal = internal
internal + confidential = confidential
confidential + restricted = restricted
但这只是保守规则。实际系统还需要处理:
- 聚合后的推断敏感度;
- 统计结果的重识别风险;
- 多字段组合;
- 模型生成的新敏感信息;
- 经过摘要后的敏感内容;
- 外部数据与内部数据的关联。
5.3 输出检查仍然不可省略
预置加固不是“只做入口控制”。最终输出仍需检查:
- 是否包含敏感字段;
- 是否包含凭证;
- 是否暴露内部路径;
- 是否泄露系统提示;
- 是否包含其他租户数据;
- 是否违反目的限制;
- 是否准备发送到未经批准的外部服务。
完整防线应为:
入口最小化
+ 访问前授权
+ 处理中标签
+ 出口检测
+ 日志审计
六、Agent 数据防泄露的完整流程
下面给出一个更接近生产系统的处理流程:
这个流程体现了三个原则:
- 数据访问前做决定;
- 工具调用过程中重复授权;
- 最终输出继续检查。
七、几个常见但危险的做法
7.1 只在系统提示词中要求“不要泄露”
例如:
你不能输出用户的身份证号、银行卡号和内部密钥。
这类约束有一定作用,但不能作为主要安全边界。
原因是:
- 模型可能误解指令;
- 上下文可能被 Prompt Injection 污染;
- 工具可能直接返回敏感数据;
- 日志系统可能在模型输出前就记录原始值;
- 子代理可能不继承相同约束;
- 不同模型的遵循能力不同。
提示词是行为指导,不是访问控制系统。
7.2 先查完整数据,再在输出阶段脱敏
这种方式的问题是:
- 敏感数据已经进入上下文;
- 可能被模型用于推理;
- 可能出现在中间日志;
- 可能传递给子代理;
- 可能进入工具参数;
- 可能在异常栈中暴露。
更合理的是在数据源头建立受限视图。
7.3 只做数据库权限,不做工具权限
即使数据库本身有权限控制,Agent 仍可能通过:
- Shell;
- 文件读取;
- Web 接口;
- 缓存;
- 导出文件;
- 其他工具;
绕过预期的数据访问路径。
因此,权限策略需要覆盖工具集合,而不是只覆盖数据库连接。
7.4 把脱敏当成万能解决方案
脱敏可以降低直接泄露,但不能解决:
- 推断攻击;
- 多字段关联;
- 频率分析;
- 业务逻辑泄露;
- 访问行为泄露;
- 其他租户数据混入;
- 高权限工具被滥用。
脱敏必须和数据最小化、访问控制、目的限制和输出审查共同使用。
八、如何落地:从一个业务场景开始
建议不要一开始就改造所有数据源,而是选择一个边界明确的场景,例如:
- 客服工单 Agent;
- 内部知识库问答;
- 代码仓库分析 Agent;
- 财务报表摘要 Agent;
- IT 运维只读 Agent。
以客服工单 Agent 为例,可以按以下步骤实施。
第一步:建立数据目录
列出 Agent 可能接触的数据:
| 数据 | 敏感级别 | 是否必要 |
|---|---|---|
| 工单编号 | 内部 | 必要 |
| 用户姓名 | 内部 | 视任务而定 |
| 手机号 | 机密 | 通常只需部分脱敏 |
| 身份证号 | 限制 | 默认不可见 |
| 地址 | 机密 | 处理配送问题时必要 |
| 支付信息 | 限制 | 默认不可见 |
| 内部风险评分 | 限制 | 默认不可见 |
第二步:定义任务目的
不同任务应使用不同数据视图:
查询物流
→ 工单编号、姓名、收货区域、物流状态
修改地址
→ 工单编号、身份核验结果、最新地址
处理退款
→ 工单编号、订单状态、退款规则
→ 不直接暴露完整支付凭证
第三步:为工具建立权限表
tools:
ticket.read:
operations: [read]
data: [ticket_id, status, summary]
approval: automatic
customer.address.update:
operations: [write]
data: [verified_customer_id, address]
approval: required
payment.refund:
operations: [write]
data: [order_id, refund_amount]
approval: human_required
第四步:建立输出策略
对于每一个出口定义策略:
返回用户
→ 允许业务需要的字段
写入日志
→ 只保存摘要和哈希
发送模型
→ 只发送必要字段
发送外部服务
→ 默认拒绝,按域名和目的授权
传给子代理
→ 重新计算权限,不直接继承全部上下文
第五步:验证拒绝场景
安全系统不能只测试“正常请求成功”,还要测试:
- 请求超出字段范围;
- 请求访问其他租户;
- 请求读取限制字段;
- Skill 尝试调用未授权工具;
- 输出包含敏感信息;
- 子代理尝试扩大权限;
- 会话恢复后重复使用过期权限;
- Agent 被恶意内容诱导。
九、建议建立的审计记录
一次数据访问至少应记录以下信息:
{
"request_id": "req-20260817-001",
"session_id": "session-123",
"actor_id": "user-456",
"agent_id": "support-agent",
"purpose": "resolve_delivery_issue",
"tool": "customer.lookup",
"resource": "customer:10086",
"requested_fields": [
"name",
"address",
"support_status"
],
"decision": "allow_with_transform",
"transform": "mask_contact",
"policy_version": "policy-2026-08-17",
"timestamp": "2026-08-17T12:30:00Z"
}
注意两点:
- 审计需要能够说明“为什么允许”;
- 审计本身不能保存未经处理的敏感数据。
建议使用:
- 字段摘要;
- 哈希;
- 脱敏值;
- 数据分类;
- 策略版本;
- 工具调用 ID;
而不是在日志中保存完整身份证号、Token 或原始模型上下文。
十、如何评价“预置加固”的效果?
可以建立以下指标:
数据暴露指标
- Agent 平均可见字段数;
- 敏感字段进入上下文的次数;
- 未使用字段进入上下文的比例;
- 子代理继承敏感上下文的次数;
- 外部服务接收敏感字段的次数。
权限指标
- 工具调用前授权覆盖率;
- 高风险工具人工确认率;
- 越权请求阻断率;
- 临时凭证回收成功率;
- 过期权限使用次数。
输出指标
- 敏感数据输出拦截率;
- 脱敏成功率;
- 误拦截率;
- 未经批准的外部发送次数;
- 日志脱敏覆盖率。
可靠性指标
- 策略服务不可用时的默认行为;
- 审计失败时是否阻断访问;
- 会话恢复后的权限一致性;
- 工具超时后的权限回收;
- 任务取消后的进程清理。
可以用一个简单的风险评估模型辅助比较:
[
R = P \times I \times E
]
其中:
- (P):发生数据越权或泄露的概率;
- (I):数据泄露影响;
- (E):Agent 可访问数据和工具的暴露范围。
预置加固主要通过降低 (E) 和部分降低 (P) 来减少总体风险;出口检测则主要降低最终泄露成功率。两者应当结合,而不能互相替代。
十一、三个关键结论
结论一:最小可见应当成为默认配置
不要先给 Agent 完整数据,再要求它“只使用必要部分”。
正确顺序是:
先判断任务
→ 再决定字段
→ 再生成上下文
结论二:授权必须发生在每次工具调用之前
Agent 的任务会动态变化,后续工具调用不一定与初始请求相同。因此,权限不能只在会话建立时检查一次。
每次访问新资源,都应重新判断:
- 身份;
- 任务目的;
- 资源;
- 操作;
- 数据范围;
- 当前策略;
- 是否需要人工确认。
结论三:预置控制不能取消出口检查
即使已经完成源头最小化,仍可能出现:
- 模型生成敏感推断;
- 多个低敏字段组合成高敏结果;
- 外部网页包含内部信息;
- 子代理返回未标记内容;
- 日志或错误消息暴露数据;
- 配置错误导致完整字段进入上下文。
因此,完整方案应当是:
数据最小化
+ 访问前授权
+ 数据流标记
+ 工具隔离
+ 出口检测
+ 审计与告警
十二、总结
AI Agent 的数据安全问题,不能只靠最后一道出口检查解决。
当敏感数据已经进入模型上下文、工具参数、子代理消息或会话日志后,后续再进行脱敏,往往只能降低部分风险,无法完全恢复数据边界。
更可靠的做法是把防线前移:
数据源头做最小化
访问之前做授权
处理过程中保留标记
工具调用使用隔离环境
输出阶段继续检查
全流程记录可审计证据
预置加固并不意味着放弃出口防护,而是让安全控制从单一的“结果拦截”升级为分层的数据生命周期治理。
对于正在建设 Agent 平台的团队,最值得优先落地的不是复杂的模型安全提示词,而是三个基础能力:
- 字段级数据视图:Agent 默认只能看到完成任务所需的数据;
- 工具级访问策略:每次调用前重新核对身份、目的和权限;
- 敏感度与来源标签:数据在模型、工具、子代理和日志之间流动时可追踪。
最终目标不是让 Agent“永远不会犯错”,而是即使 Agent 受到错误指令、恶意内容或配置异常影响,也不能轻易获得超出任务所需的数据和能力。
参考资料
Data Leakage Prevention in Agentic Applications via Preemptive Hardening,arXiv:2607.18847SlotGuard,arXiv:2607.17147- NIST AI Risk Management Framework
- OWASP Top 10 for LLM Applications
- OWASP Agentic Applications Security Guidance
版权声明:本文为原创技术文章,允许在注明作者与出处的前提下转载。
更多推荐




所有评论(0)