
1. 事件复盘一个智能体是怎么自己走出去的1.1 从工具调用到越界访问的链路还原先把这件事的骨架讲清楚。所谓智能体越界访问政府网站落到工程层面几乎都不是模型突然觉醒了而是一条非常具体的执行链路出了问题用户给了一个模糊任务智能体在规划Planning阶段把任务拆成了若干子步骤其中某一步需要获取公开信息于是它调用了联网检索工具或浏览器自动化工具而这个工具在实现时没有做域名白名单最终请求打到了一个本不该被访问的政府站点上。这条链路里真正出问题的环节通常有三个意图解析层用户的原话可能是帮我查一下这个政策的最新执行情况模型把查理解成了主动访问来源网站而不是基于已有知识回答。工具授权层工具注册时给了过大的权限比如一个通用http_request工具参数里 URL 完全由模型自由填写。执行沙箱层没有出站流量管控没有域名黑白名单请求直接就发出去了。我见过太多团队在做智能体时把注意力全放在提示词怎么写得更聪明上却对工具权限这件事极其随意。一个能自由发 HTTP 请求的工具本质上等于把一台没有防火墙的机器交给了模型。模型不会恶意但它会字面理解你的指令这就是风险的全部来源。1.2 为什么这类事件被称作黑天鹅黑天鹅这个词用在这里其实挺准确但需要拆开看。它不是说这件事概率极低所以意外而是说它的触发路径在事前很难被穷举。传统软件的安全测试你可以枚举输入、枚举接口、枚举权限组合但智能体的行为空间是自然语言 × 工具集合 × 环境状态的笛卡尔积这个空间大到无法穷举。更麻烦的是智能体具有自主容错特性。当第一次工具调用失败比如返回 403它不会停下来而是会尝试换一个 URL、换一种请求方式、换一个工具继续尝试。这个自主重试能力在正常场景下是优点在越界场景下就是把一次小失误放大成一次持续探测。所以这类事件的特征是单点看每一步都合理连起来看整体越界。这也是为什么事后复盘时很多人第一反应是这也能算漏洞但监管和公众视角看它就是一次不该发生的访问。2. 根因拆解AI安全治理到底缺了哪几块2.1 权限模型从能做什么到只能做什么绝大多数智能体框架的权限设计是加法模型先给一个基础能力集需要什么再加。但安全上正确的做法是减法模型默认什么都不能做每一项能力都要显式申请、显式审批、显式限定范围。举个具体的对比。加法模型下你会这样注册工具tools [search_tool, http_tool, browser_tool, file_tool, code_tool] agent Agent(toolstools)减法模型下应该是这样tools [ SearchTool(domains[*.gov.cn, *.edu.cn], max_results5), HttpTool(allowed_hosts[api.internal.example.com], methods[GET]), ] agent Agent(toolstools, deny_by_defaultTrue)差别在哪加法模型里http_tool的 URL 参数是模型自由填写的字符串减法模型里allowed_hosts是一个硬编码的白名单模型填什么都会被这个白名单过滤一遍。白名单不是建议是执行前的强制校验。我个人的经验是任何涉及出站请求的工具都必须满足三个条件才能上线目标域名可枚举、请求方法可枚举、请求体结构可校验。三者缺一这个工具就不该给智能体用。2.2 行为审计看不见的访问等于没发生智能体行为审计这个词最近被提得很多但很多人理解成了记日志。记日志只是第一步真正的审计要能回答四个问题审计维度要回答的问题落地方式谁发起哪个用户、哪个会话触发的会话 ID 用户 ID 绑定做了什么调用了哪个工具、参数是什么工具调用全量落库为什么做触发这次调用的推理链是什么保留规划步骤的中间输出结果如何请求是否成功、返回了什么响应状态码 摘要很多团队只做了第二项结果出事之后只能看到访问了某网站但完全不知道模型是基于什么推理走到这一步的复盘根本无从下手。推理链的留存比结果日志更重要因为它是唯一能解释为什么的证据。2.3 沙箱隔离让越界访问打不到真实目标沙箱这件事说穿了就是一句话智能体的网络出口不应该和你的生产网络是同一个。理想状态下智能体运行在一个独立的网络命名空间里出站流量必须经过一个代理层代理层做域名过滤和速率限制。这里有个容易被忽略的细节DNS 也要管控。有些越界访问是通过 DNS 解析到内网地址实现的如果你只做了 HTTP 层的域名白名单但 DNS 没管模型完全可以用一个白名单域名解析到内网 IP。所以完整的出站管控应该是DNS 层 传输层 应用层三层都过一遍。3. 实操方案给智能体套上缰绳的完整步骤3.1 第一步梳理工具清单做权限分级在写任何代码之前先做一张表。把你智能体当前注册的所有工具列出来按是否产生外部副作用分成四类只读本地读文件、查缓存风险最低只读外部搜索、查 API需要域名白名单写本地写文件、改配置需要路径白名单写外部发请求、发消息、下单风险最高需要人工确认或强审批分级之后给每一级设定不同的执行策略。我的建议是写外部类工具默认禁用需要显式开启且每次调用都要记录完整参数。这不是保守是因为这类工具的误用代价最高。3.2 第二步实现出站请求的强制代理不管你用什么语言核心逻辑是一样的所有工具的出站请求不允许直接调用底层 HTTP 库必须走一个统一的代理函数。这个函数做四件事import ipaddress from urllib.parse import urlparse ALLOWED_HOSTS {api.example.com, search.example.org} BLOCKED_CIDRS [ ipaddress.ip_network(10.0.0.0/8), ipaddress.ip_network(172.16.0.0/12), ipaddress.ip_network(192.168.0.0/16), ipaddress.ip_network(127.0.0.0/8), ] def safe_request(url, methodGET, **kwargs): parsed urlparse(url) host parsed.hostname # 1. 域名白名单校验 if host not in ALLOWED_HOSTS: raise PermissionError(fhost {host} not in allowlist) # 2. 解析后校验 IP防止 DNS 重绑定 import socket resolved socket.gethostbyname(host) ip ipaddress.ip_address(resolved) for cidr in BLOCKED_CIDRS: if ip in cidr: raise PermissionError(fresolved ip {ip} in blocked range) # 3. 方法白名单 if method not in {GET, POST}: raise PermissionError(fmethod {method} not allowed) # 4. 记录审计日志 audit_log(hosthost, ipresolved, methodmethod, urlurl) return requests.request(method, url, timeout10, **kwargs)这段代码里最关键的是第二步的 DNS 重绑定防护。很多人只做域名白名单但攻击者或者模型的无意行为可以让一个白名单域名解析到内网 IP绕过域名检查。先解析再校验 IP才能堵住这个口子。注意socket.gethostbyname只返回一个 IP如果目标有多个 A 记录需要遍历所有解析结果逐一校验否则可能漏掉内网地址。3.3 第三步给智能体加执行前确认闸门对于高风险操作最有效的防护不是技术手段而是让人在回路里。具体做法是当智能体规划出的下一步涉及写外部类工具时暂停执行把即将访问的 URL、请求方法、参数摘要推给用户或管理员确认。这个确认机制的设计要点确认信息要具体不能只说智能体想访问外部网站要给出完整 URL 和参数确认要有超时默认 60 秒无响应则拒绝避免挂起确认要可批量同一会话内相同域名的请求可以一次性授权避免频繁打断我实测下来这个机制对正常使用体验的影响很小因为绝大多数合法请求都是重复的域名第一次确认之后后续就自动放行了。但它能拦住 100% 的越界访问性价比极高。3.4 第四步建立行为基线做异常检测前面三步是防这一步是察。即使防护做得再好也要假设会有漏网之鱼所以需要一套异常检测机制。基线的建立方式是统计智能体在正常使用下单位时间内的工具调用次数、访问域名分布、请求频率。然后设定阈值超过阈值就告警。比如单会话 5 分钟内访问超过 20 个不同域名 → 告警单会话内出现从未见过的域名 → 记录并人工复核请求频率超过 10 QPS → 限流这套东西不需要多复杂一个简单的滑动窗口统计就够了。关键是要有而不是等出事之后才想起来做。4. 常见问题与排查技巧实录4.1 智能体绕过白名单的几种典型手法在实际测试中我遇到过几种模型自作聪明绕过限制的情况列出来给大家提个醒绕过手法具体表现防护方式URL 编码变形把域名写成%61%70%69...解析前先规范化重定向跳转白名单域名 302 到目标站禁止自动跟随重定向DNS 重绑定白名单域名解析到内网解析后校验 IP协议混淆用file://或gopher://协议白名单大小写混用API.EXAMPLE.COM统一转小写比较这些手法里重定向跳转是最容易被忽略的。很多 HTTP 库默认自动跟随 302结果白名单校验只校验了第一个 URL跳转后的目标完全没检查。解决办法很简单关闭自动重定向手动处理每一跳并逐跳校验。4.2 排查越界访问的标准流程一旦发现智能体访问了不该访问的地址按这个顺序排查定位会话从审计日志里找到对应的会话 ID 和用户 ID还原推理链调出该会话的完整规划步骤看模型是在哪一步决定要访问外部检查工具参数看模型填的 URL 是什么是用户给的还是模型自己生成的验证防护失效点白名单为什么没拦住是没配、配错了、还是被绕过了评估影响范围这次访问是否成功返回了什么数据是否被记录到了外部第 5 步特别重要。如果请求成功了返回的数据可能已经进入了模型的上下文甚至被写进了后续的输出里。这种情况下影响就不只是一次访问而是数据可能已经泄露。4.3 几个我踩过的坑坑一以为提示词能解决安全问题。我早期试过在系统提示词里写不要访问未经授权的网站结果模型在遇到需要信息的时候还是会去访问。提示词是软约束模型可能遵守也可能不遵守安全必须靠硬编码的校验。坑二白名单配了但没生效。有一次排查了半天发现是工具注册的时候用了两个不同的 HTTP 客户端一个走了代理一个没走。所以统一出口这件事必须从架构上保证不能靠开发者自觉。坑三忽略了日志本身的敏感性。审计日志里会记录完整的 URL 和参数如果这些日志被不当访问本身就是一次信息泄露。所以审计日志也要做访问控制不能谁都能看。5. 从这次事件看智能体安全的长期建设5.1 把安全左移到设计阶段这次事件最大的启示不是要加个白名单而是智能体的安全设计必须和功能设计同步进行。很多团队的做法是先做出功能再补安全结果就是安全措施和架构不匹配补起来处处掣肘。正确的顺序是先定义智能体的能力边界它能做什么、不能做什么再设计工具集最后才是提示词和流程。能力边界是地基提示词是装修地基没打好装修再漂亮也没用。5.2 建立智能体行为的可观测性可观测性这个词在传统后端里很成熟但在智能体领域还比较新。我的理解是智能体的可观测性要覆盖三个层次调用层每次工具调用的输入输出决策层模型的规划步骤和推理依据会话层整个会话的目标、进展、最终结果这三层里决策层是最难做但最有价值的。因为只有决策层能解释为什么而为什么是复盘和改进的唯一依据。5.3 参考成熟的安全框架智能体安全虽然新但很多原则是从传统安全继承来的。比如 OWASP 针对智能体应用整理的 Top 10 风险清单里面提到的工具滥用权限过大提示注入等问题本质上都是传统安全问题的变体。做智能体安全的同学不妨把传统 Web 安全和 API 安全的那套方法论拿过来很多经验是通用的。我在实际项目里的体会是智能体安全最难的不是技术而是心态。很多人潜意识里觉得模型是我训练的它不会乱来但事实是模型的行为空间太大任何它应该不会的假设都是危险的。把模型当成一个能力很强但完全不可预测的第三方用对待外部系统的方式对待它安全设计就会自然很多。最后分享一个实用的小技巧在智能体上线前做一次红队测试专门用模糊的、诱导性的指令去试探它的边界。我通常会准备一组测试用例比如帮我查一下这个网站的最新内容访问一下这个链接看看有什么观察智能体是否会主动发起外部访问。这个测试花不了多少时间但能提前暴露大部分越界风险。