跳转到主要内容

浏览器 Agent 会话泄漏:当单个 Profile 服务于多个租户时

阅读需 1 分钟Tian PanTian Pan

一个计算机使用型智能体(computer-use agent)在客户的 CRM 上完成了一项任务,工作线程池将浏览器返回到空闲环中,几百毫秒后下一个请求到达,仪表板导航成功——唯一的问题是,它是作为错误的用户登录成功的。前一个会话的 OAuth cookie 仍留在配置文件(profile)中。追踪记录显示 navigation succeeded(导航成功)、screenshot captured(截图已捕获)、action performed(操作已执行)。运行日志中没有任何内容表明,智能体正在以一个从未授权过它的用户身份进行操作。

这是浏览器智能体从其构建所用的库中悄然继承的一类故障。无头浏览器(headless browser)框架被设计为每个配置文件仅供一个用户使用,因为这是浏览器三十年来的工作方式。当工作池为了摊销全新的 Chromium 实例长达八秒的冷启动时间而重用配置文件时,这种“单用户”假设就破裂了,而且这种破裂对于团队通常信任的每一层遥测数据来说都是不可见的。

为什么要重用配置文件

冷启动一个干净的浏览器代价高昂。一个加载了智能体所需的所有扩展程序的全新 Chromium 进程需要几秒钟才能渲染出第一个页面;对于一个延迟预算低于两秒的面向用户的智能体来说,这并不是一个你可以在每个请求中支付的启动成本。因此,工程上的本能反应与自连接池诞生以来每个 Web 平台所使用的反应是一样的:保持一个包含 N 个浏览器的热池(warm pool),每个请求分配一个,请求完成后将其归还。

问题在于,数据库连接池中的“连接”除了 TCP 套接字之外没有持久状态。而浏览器池中的“浏览器”拥有三十年积累的状态原语——cookie、localStorage、sessionStorage、indexedDB、service-worker 缓存、HTTP 缓存、自动填充数据、扩展存储、下载的文件、密码管理器状态。浏览器自身的设计假设有一个人类坐在它面前,它提供的每一种状态机制都是为了服务于该人类的会话连续性。在不同请求之间重用配置文件,意味着你在没有告知浏览器的情况下颠覆了这一假设。

这种重用模式以几种形式出现,但都具有相同的潜在风险:

  • 一个长期运行的 Playwright BrowserContext 实例池,每个实例被分配给队列中的下一个请求。
  • 一个完整的浏览器进程池,其 --user-data-dir 指向一个持久目录,以便登录会话在重启后依然存在。
  • 支持计算机使用型智能体的“热车队”(warm fleet)服务,其池大小根据峰值并发量确定,且配置文件在请求之间可能会保持数小时的活跃状态。

在每种情况下,前一个租户积累的凭据在下一个租户的请求开始时,仍然留在磁盘和内存中。

浏览器在调用之间携带的状态面

那些没有处理过无头浏览器安全方面的工程师,往往会低估执行 page.close() 后残留的内容。清单比看起来要长:

  • Cookie,包括 HTTP-only 和安全 cookie——它们属于配置文件,而非选项卡。
  • 前一个请求涉及的每个源(origin)的 localStorage 和 sessionStorage
  • IndexedDB,SaaS 应用越来越多地在这里存放 JWT、会话元数据和离线缓存。
  • Service-worker 缓存,它可以在不访问网络的情况下提供前一个租户的经过身份验证的响应。
  • HTTP 缓存,它可以绕过重新认证流程,并从磁盘提供已登录的仪表板。
  • 扩展存储,包括密码管理器的自动填充数据和 SSO 辅助状态。
  • 下载的文件和文件选择器的工作目录
  • 自动填充配置文件——姓名、地址、支付方式——下一个请求可能会意外地将其提交到表单中。

请求之间的“会话边界”必须清除上述每一项,而不仅仅是 cookie 罐。大多数自研的重置程序只清除前三项,而忽略了其余项。尤其是 Service-worker 缓存很容易被忽视,因为它们是页面离线行为的一部分,而不是明显的“会话”层面。

像 Playwright 这样的框架明确说明,创建一个全新的 BrowserContext 是受支持的隔离原语——上下文的设计初衷是创建成本低廉,且在单个浏览器进程中彼此完全隔离。陷阱在于,从测试领域学习这种模式的团队往往会将其错误地映射到他们的智能体基础设施上:在测试中,框架会为每个测试创建和销毁上下文,因此隔离是自动的。而在智能体工作池中,该生命周期必须由团队强制执行,且“我们使用 Playwright 上下文”并不等同于“我们为每个请求销毁并重建上下文”。

追踪记录未显示的故障模式

当发生会话泄露(session bleed)时,智能体自身的观测系统不会提示任何错误。导航返回了 200。页面已渲染。截图已捕获。DOM 操作已成功。模型根据其看到的内容生成了连贯的响应。从智能体的角度来看,该请求是成功的。

会员专享

余下内容仅对会员开放。

会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。

  • 完整文章,包含未公开存档的部分
  • 可落地的工作框架,附带权衡与决策依据
  • 新文章抢先看,先于公开发布

随时取消 · 一次订阅,畅读全部

参考资料

保持联系,关注我获取更多内容

阅读需 9 分钟

当 On-Behalf-Of 悄然变成 Act-As:你的智能体继承的 OAuth 作用域陷阱

一个持有用户形态 OAuth 令牌的 AI 智能体正在冒充该用户,无论安全审查如何称呼它。提示词注入是如何将这一间隙演变为安全事件的,以及修复该问题的令牌级模式。

insider
security
阅读需 8 分钟

你的工具描述是模型遵循的指令通道

工具描述是模型视为权威指令的散文,但代码审查和输入清理从未检查过它们。本文将探讨被投毒的元数据和地毯式攻击是如何渗透进来的,以及弥合这一差距的规范。

insider
ai-engineering
阅读需 10 分钟

Token 放大:烧掉你账单的提示词注入攻击

大多数提示词注入威胁模型都集中在数据泄露上。更隐蔽的一类攻击是账单放大 —— 0.01 美元的请求变成了 40 美元的推理发票。这里是阻止该攻击的防御准则。

insider
ai-engineering
阅读需 12 分钟

语义缓存是安全隐患,而非性能提升

语义缓存能在不到一毫秒的时间内返回另一个用户的响应,而你的命中率仪表盘还会因此变绿。本文探讨如何通过缓存键设计、溯源封装和审计追踪,从架构层面防止跨用户数据泄漏。

insider
llm
阅读需 9 分钟

多用户 AI 会话:没人在设计阶段考虑的上下文归属问题

当多个用户共享一个 AI 助手时,上下文就变成了一个没有访问控制的共享可变资源。本文探讨上下文泄漏、个性化污染以及团队规模下出现的竞态条件,以及真正能预防这些问题的隔离模式。

insider
ai-engineering