浏览器 Agent 会话泄漏:当单个 Profile 服务于多个租户时
一个计算机使用型智能体(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 操作已成功。模型根据其看到的内容生成了连贯的响应。从智能体的角度来看,该请求是成功的。
- https://playwright.dev/docs/browser-contexts
- https://docs.browserless.io/enterprise/user-data-directory
- https://www.cloudflare.com/learning/access-management/what-is-browser-isolation/
- https://arxiv.org/html/2505.13076v1
- https://www.paloaltonetworks.com/blog/sase/ai-and-the-new-browser-security-landscape/
- https://e2b.dev/pricing
- https://web.dev/learn/pwa/offline-data/
- https://chameleonmode.com/cookie-and-session-management-5-critical-isolation-requirements/
