AI智能体帮你操作浏览器的时候,密码到底该不该给它?

传统做法是让模型直接键入密码,但这样一来,敏感信息就不可避免地进入了智能体的上下文。Playwright、Anthropic的Computer Use这类工具都存在这个问题。就算用占位符策略在提示词里隐藏值,智能体进程本身仍然持有数据,提示词注入攻击还能重定向表单提交。

打开网易新闻 查看精彩图片

1Claw开源的browser-bridge项目换了个思路:让AI智能体驱动浏览器,但永远看不到密码。后端进程在智能体从未操控的页面上完成输入和凭证捕获,整个机制靠CDP白名单、作用域化的Chromium上下文,以及一个拒绝所有序列化路径的SecretHandle类来强制执行。

核心不变量:后端进程不能通过工具结果返回任何密钥

这个项目的核心设计是把密钥保存在智能体无法读取的独立进程中。智能体只能询问"绑定哪一个",而不会收到实际值。架构上分四层:智能体假装自己是Chromium连接并调用填充请求;bridge拥有浏览器,在智能体从未操控的页面上键入凭证;后端负责策略决策与密钥保管;Chromium通过管道启动,不需要调试端口。

不变量被沉淀在核心层,四个驱动各自负责密钥存储位置、策略评估权、审计流向与能力边界。构建时强制测试会在核心引用任何驱动时失败,这样不变量只需要审查一次,而不是每个驱动都审查一遍。

SecretHandle:把密钥泄露从"格式化错误"变成"控制中断"

SecretHandle类是这套机制的强制执行者。它让密钥泄露从"格式化错误"变成"控制中断":JSON.stringify抛出异常,字符串强制转换屏蔽内容,inspect API同样屏蔽,对象键与展开操作返回空——因为字节数据是真正的私有字段。

由于JavaScript没有析构函数,项目用using配合Symbol.dispose或handle.use(fn)确保在throw路径上也能完成释放。这正是实时密钥容易遗落的地方。

CDP权限:框架连接到Chromium的唯一通道

bridge是连接到Chromium的唯一进程。框架只能通过一道门控与之通信,门控仅允许bridge的命令与事件。如果智能体维持自己的CDP连接,不变量会在一步之内失效——填充完成后它可能读取input.value、无障碍树、截图或Network日志。

门控采用白名单而非会过时的黑名单,并在填充期间阻断整个目标,直接丢弃推送事件而不是排队。隔离机制是per-client的Chromium BrowserContext,而非基于过滤。每个客户端拥有独立的BrowserContext,每条命令中对目标或session的引用都会与该客户端的权限进行比对:getTargets被限制为仅返回调用方自己的页面,对他人页面执行attachToTarget即便ID已知也会被拒绝。

一个测试通过同一个真实Chromium的两个socket驱动该序列,并断言其必然失败。仅靠过滤响应无法消除泄露,必须对命令与事件进行门控。

凭证捕获:读取站点展示一次的API密钥

begin_credential_capture通过人类编写的人类审核策略工作:bridge导航到目标页、触发生成、通过选择器读取值并存入保险库。智能体仅传递site_id与target_id——无法选择URL、生成控件、值元素或条目。

这弥补了登录后的缺口:许多站点会颁发智能体需要却不应看见的token。受控注册流程同样防止智能体自行选择凭证的创建位置。bridge可注册用户账号、生成密码并存入——智能体仅传一个参数(site_id)调用工具,收到的是绑定ID而非密码。主机、注册URL、用户名与选择器均由人类编写的人类审核策略决定。提交与键入是独立的:bridge等待成功信号,若无则取消而非存储。四个端到端测试基于真实Chromium强制执行这一流程。

这套设计解决的是什么问题?

Computer Use让模型发出击键动作,以此方式输入的密码是一个模型选择的操作,因此会进入其上下文。即便采用sensitive_data占位符策略,也只是在提示词中隐藏了值,智能体进程仍持有该数据,提示词注入仍可重定向表单提交。

browser-bridge的架构让智能体询问应绑定哪一个,它不能选择页面,不能读取字段,也不能收集值。后端可以拒绝填充,没有任何机制能扩大允许范围。这里不存在让模型自我监管的需求,因为故障模式是摄像头级别的。

核心在所有后端之上强制执行不变量,且一旦核心引用驱动,构建即失败。这套设计把安全从"依赖模型自律"变成了"架构上不可能泄露"。