ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Computer Use三维门禁设计:应用、动作、数据全链路安全防护

Computer Use三维门禁设计:应用、动作、数据全链路安全防护

我第一次把Computer Use公共预览的demo跑起来时,第一反应其实不是“好厉害”,而是一阵紧张。原因很简单:模型正在看着我的屏幕,自己决定把鼠标挪到哪个按钮、在输入框里敲什么、甚至提交什么表单。这跟我以往调过的任何LLM应用都不一样——以前模型说错话,危害大概率停留在文本层面;现在模型做错事,是真实环境里的一次点击、一次写入、一次删除。

所以从那天起,我思考的重点就不是“这个功能还能做什么”,而是“这套玩法在公共预览阶段到底怎么设计才不会出事”。这个功能的能力链路很短:系统截取屏幕画面,模型理解界面,输出鼠标键盘动作,环境执行动作,再看结果。链路越短,安全设计越不能依赖单一开关。这也是我后来总结出“应用门禁、动作门禁、数据门禁”三维设计思路的起点。下面我把这套思路完整拆开,讲讲每一层到底在防什么、怎么落地,以及我在实测中踩过的坑。

1. Computer Use为什么是安全设计的高压区——先想清楚威胁模型

Computer Use这类能力最大的变化,是把模型从“建议者”变成了“操作者”。以前我做一个RAG应用,模型再离谱也只是在输出文本里给你一段错误回答,数据泄露通常也发生在生成内容里;但现在模型直接输出操作指令,指令本身就会改变系统状态。这种从“识别”到“操作”的跃迁,是整个安全设计的原点。

1.1 从“识别”到“操作”:能力跃迁带来的风险质变

你仔细想一下Computer Use的执行链路就会明白:首轮模型拿到屏幕截图,它要识别当前界面、理解用户意图,然后输出一串控制指令,比如移动鼠标到某个坐标、左键单击、输入文本。执行环境收到指令后,在真实的桌面上完成操作,然后截取新画面继续下一轮循环。

这个过程中有几个非常要命的地方。

第一,操作对象是真实环境。模型点击的按钮、打开的软件、写入的文件,都是实实在在的实体资源。哪怕只是注册表、配置目录、文件权限被改动,也可能让系统状态不可预期。

第二,大量操作是不可逆的。删除文件、发送消息、下载并执行程序、覆盖配置、提交订单,这类动作一旦落地就无法简单回退。以前对模型的安全控制主要围绕“输出内容”,现在必须围绕“行为动作”。

第三,模型的上下文里天然包含了高敏感信息。屏幕帧本身就是信息载体,账户页面、客户资料、内部聊天记录,都会成为模型推理的输入。这不只是“提示词注入”的问题,而是数据暴露面被无限放大。

我经常用一句话类比:以前的模型是顾问,它只负责出主意,做不做决定权在人类;Computer Use是实习生,手已经放在鼠标和键盘上了。你可以口头叮嘱实习生“别乱动”,但真正可靠的做法是给他一个权限受限的工作台、一套需要审批的动作流程、一个无法把数据带出去的出口。这也是三维门禁存在的意义。

1.2 公共预览的三种“先天不足”

公共预览阶段做安全设计,还要额外承认三件事。

一是模型行为边界尚未收敛。我在真实任务里见过它突然去做和任务无关的浏览行为,也见过它对一个弹窗进行反复尝试。公共预览不是正式版,模型的决策分布还在变化,计划外动作几乎无法完全避免。如果你把“模型应该不会做危险操作”当作安全前提,那迟早会翻车。

二是安全生态工具链完全不成熟。Computer Use出来时,社区里并没有一套开箱即用的“Agent安全网关”,官方更多也是在文档里建议开发者自行做好沙箱和权限控制。这意味着你必须自己组装门禁体系,而不是靠某个现成产品一键搞定。

三是交互链路太长,暴露面太多。截图流、动作流、网络流、文件流,每一段都会引入新的攻击点:截图的泄露风险、动作的误操作风险、网络出口的数据外送风险、容器本身的逃逸风险。只盯着其中一条链路做防护,其他链路就一定会出问题。

这也是为什么我把安全设计拆成“应用、动作、数据”三个维度。每一维都是在管一条关键链路,合在一起才构成真正的纵深。只靠一层的方案,在公共预览这种高不确定性阶段,都太脆弱。

2. 应用门禁:先把Agent关进一个“只装着必要工具的房间”

应用门禁是我最先落地的一层,因为它最直接、最容易实现,而且能把大量威胁挡在源头。思路很简单:模型能操作什么,取决于环境里存在什么。环境里压根没有某个应用,模型根本没机会调用它。

2.1 为什么第一个关口非要是“应用”

你想想Computer Use的工作方式就能理解:模型的指令基于屏幕画面,它要去“打开邮件”“发送消息”,前提是桌面环境里有邮件客户端,并且界面可见。如果运行环境里只装了浏览器、文本编辑器、终端这些任务必需软件,邮箱客户端、网盘同步工具、内部系统快捷键一个都没有,那模型就算被提示词诱导,也无处下手。

所以我做应用门禁的时候,第一件事就是列“任务白名单”——这个Agent会话只被允许使用哪些应用。白名单之外的软件一律不安装、不配置、不出现在应用菜单里。这一步虽然听起来简单,但效果非常强。很多公共预览的演示demo出问题,都是因为环境里装了一堆跟任务无关的工具,模型在探索过程中“误触”了它们。

除了能安装哪些软件,应用门禁还要管系统权限。Agent运行用户必须是低权限用户,不该有sudo、不该有管理员账号、不该能随意修改系统级的配置目录。给Agent一个普通用户身份和一台干净的容器环境,是应用层的安全底线。

2.2 用容器和可丢弃镜像打造运行沙箱

“可丢弃”是我做沙箱时特别强调的一个理念。公共预览阶段的Agent行为太不可控,与其研究怎么从脏环境里“恢复现场”,不如每次任务都从干净的镜像重新启动环境。容器天然适合这件事。

我当时的做法很简单,用Docker镜像定义Agent的整套运行环境,只装任务必要的软件。一个最基础的Dockerfile长这样:

FROM ubuntu:24.04 RUN apt-get update && apt-get install -y \ firefox \ vim \ curl \ ca-certificates \ && useradd -m -s /bin/bash agent \ && mkdir -p /workspace && chown agent:agent /workspace USER agent WORKDIR /workspace

启动容器的时候,我一般会加一层限制参数:

docker run --rm \ --name computer-use-agent \ --network sandbox-net \ --cap-drop=ALL \ --security-opt no-new-privileges \ --read-only \ --tmpfs /tmp:rw,noexec,nosuid,size=512m \ -v /srv/agent-workspace:/workspace:rw \ computer-use-agent:latest

逐项解释一下这些参数的意义。--read-only让根文件系统变成只读,Agent在里面改不了系统文件;--cap-drop=ALL丢掉所有内核能力,避免提权;--security-opt no-new-privileges禁止进程获得新权限;--tmpfs限制临时目录大小且不允许执行代码;--network sandbox-net把容器放进一个隔离的自定义网络里,后续网络策略都在这个网络上做。这些参数单独看都只是“加固”,合在一起是把Agent关进一个装修得非常简单的房间。

工作目录虽然是可写的,但我会定期清空,并且保证宿主侧只有运维人员能读取。因为Agent在容器内写出的任何文件,都有可能是被诱导生成的恶意脚本,我不会让自己的工作机直接去执行它。

2.3 浏览器依然是最危险的一扇窗:URL白名单与出网隔离

容器应用门禁做到位,剩下最大的风险窗口就是浏览器。模型的大量操作都需要浏览器完成——查文档、读表格、点链接,但浏览器一旦放开,等于把整个互联网都塞进了Agent的视野。钓鱼页面、恶意脚本、验证码陷阱、含误导指令的网页,都会变成模型的攻击入口。

我的处理方式是在容器网络出口上架一个正向代理,由代理维护URL白名单。模型在容器里访问任何地址,都必须经过这个代理,代理只放行预先登记的域名,其他一律返回拒绝。同时我在宿主侧用iptables把容器网络的出口做第二次拦截,确保即使某个进程不走代理,流量也出不去:

iptables -P OUTPUT DROP iptables -A OUTPUT -o lo -j ACCEPT iptables -A OUTPUT -p tcp --dport 3128 -m owner --uid-owner squid -j ACCEPT

这里有个关键点:只配浏览器代理是不够的。模型完全可能通过终端里的curl命令发起请求,绕开浏览器配置。所以我把网络限制放在内核层,容器进程能出去的路径只有“连接到代理服务器的3128端口”这一条,其他全部静默丢弃。这其实很像电气设备里的安全回路设计,每个关键路径都必须有自己独立的连锁保护,不能只依赖某个开关或某一层配置。

URL白名单本身不用太复杂,但原则是宁严勿松。公共预览阶段做PoC时,我给项目的白名单就只有几个指定文档域名和API端点,其他域名默认拒绝。等Agent跑通之后再去加,比出了事故再补救要便宜得多。

3. 动作门禁:让每一次点击、输入都经过“审批”闸口

应用门禁把“能操作什么软件”限定住了,但模型在允许的应用内部依然可以做很多事。比如浏览器里可以提交表单、可以下载文件、可以删除云上文件。动作门禁的职责,是管住“具体动作”,而不是只管“应用边界”。

3.1 动作分级:什么东西能自动放行,什么东西必须停下问人

Computer Use模型输出的动作一般可以归类成几类:鼠标移动、左键点击、右键点击、拖拽、滚轮、键盘输入、组合键、滚动、返回等。不同动作的破坏力差异巨大,所以我做了一个简单的动作分级表,让执行器根据等级决定策略。

等级动作示例默认策略
L1 浏览级鼠标移动、滚动、查看界面自动执行
L2 交互级点击普通链接、填写表单字段自动执行,但目标必须通过URL/应用校验
L3 敏感级提交表单、下载文件、删除文件人工审批,审批通过才执行
L4 危险级执行系统命令、访问密钥目录、修改系统配置默认拒绝,不支持放开

这里有一个很容易忽略的细节:动作门禁不能只看“动作类型”,还要看“动作目标”。同样是单击,落在普通页面上可以自动放行,落在“删除确认”“发送邮件”按钮上就是高危动作。所以我在执行器里会记录每个动作的坐标位置和对应窗口,再结合窗口标题、页面上下文判断动作的敏感度。

3.2 在模型与环境之间埋一层审批中间层

动作门禁的核心,是在模型输出和环境执行之间插入一个“审批中间层”。这个中间层接收模型返回的动作指令,解析后先跑一遍规则,再决定是放行、进入人工审批队列还是直接拒绝。

我当时的实现思路类似下面这段伪代码:

class ActionFilter: def __init__(self, allowlists, human_approver): self.allowlists = allowlists self.human_approver = human_approver def check(self, action): grade = self.grade_of(action) # 危险动作直接拒绝 if grade == "L4": return Decision.REJECT # 敏感动作进入人工审批队列 if grade == "L3": return Decision.PENDING # 低等级动作,还需要联合校验应用和网络白名单 if not self.allowlists.is_app_allowed(action.target_app): return Decision.REJECT if not self.allowlists.is_url_allowed(action.target_url): return Decision.REJECT return Decision.ALLOW

审批中间层必须放在本地环境侧,不要只依赖模型服务端策略,原因很简单:本地才能真正拿到上下文(目标窗口、页面标题、动作前截图),延迟也更低。人类审批员看到的不是一条干巴巴的“动作类型”,而是“这个动作要操作的目标是什么”“动作之前屏幕是什么样”,这时审批才真的有意义。

人工审批队列还需要设计超时自动拒绝:如果10秒内没人确认,敏感动作默认不执行。千万不要做“一键全放行”按钮,一旦审批人开始养成无脑放行的习惯,这套动作门禁就形同虚设了。

3.3 审计日志:留给事后还原现场的“黑匣子”

公共预览阶段的Agent事故,很多都是不可复现的偶发行为,等你想复盘时已经晚了。所以我把审计日志设计成“黑匣子”模式,记录的不是单条日志,而是可以还原整个现场的证据链。

我一般会为每条动作记录这些字段:

字段说明
时间戳动作发生时间
会话ID所属Agent会话
模型推理ID对应模型哪一次推理
动作类型click/type/scroll等
目标对象目标窗口标题、URL、进程名
敏感度分级L1-L4
决策结果allow/deny/pending/approved
审批人与审批意见人工确认记录
执行状态是否实际执行成功
动作前截图执行动作前屏幕关键帧
动作后截图执行动作后屏幕关键帧

前后截图是关键,只记文字日志根本说不清当时Agent到底看到了什么、为什么这么做。通过前后帧对比,很多“奇怪行为”立刻就有了解释依据。我把这些日志统一加密存放在独立的日志存储里,只有运维同学能读,并且设了保留周期,避免敏感帧长期滞留。

4. 数据门禁:把进出Agent的每一条数据都过一遍“安检”

前两重门禁管的是“能用哪些软件”和“能做什么动作”,数据门禁管的是“数据能不能进来、能不能出去”。在这个维度上,我重点看三股数据流:屏幕数据流、网络数据流、数据滞留与缓存。

4.1 屏幕数据流:模型“看到”的每一帧都是信息出口

Computer Use最特殊的地方,就是屏幕帧是模型的主要输入。这也意味着,你的屏幕内容会进入大模型的上下文。如果屏幕上出现了生产环境的密钥、客户身份证号、内部系统聊天记录,这些数据就相当于以截图形式被送出了环境。

屏幕数据流的管控我总结了三件事。

第一,使用独立虚拟显示器。Agent的会话全部运行在容器内的虚拟显示器上,宿主机的真实屏幕、宿主上的任务栏、通知弹窗,模型一律看不到。

第二,限制屏幕工作区。把需要Agent操作的任务窗口放进专门的工作区,敏感窗口可以通过窗口管理器放到另一个虚拟工位上,Agent看不到那个区域。

第三,在抓帧之前做程序化脱敏。我的做法是在截屏模块里维护一个遮挡区域列表,每次抓帧前先把临时通知、地址栏以外的敏感UI元素遮盖掉,再发给模型。这样即使页面里混入一些不该出现的内容,模型拿到的图像也已经被处理过。

分辨率也需要控制。分辨率太高,截图里的小字、背景信息都会非常清晰,脱敏难度急剧上升;分辨率太低,模型又看不清必要的按钮和文字。我在PoC阶段一般锁在1024x768附近,既能保证模型基本识别,又不会把无关细节暴露得太彻底。

4.2 网络数据流:出口白名单是防泄漏的最后硬墙

屏幕脱敏帮我们拦截了“可见”的信息携带,但真正要把数据带出去的通道,最终都要落到网络。即使有了应用门禁和动作门禁,一个被诱导生成的恶意脚本,仍然可能在后台尝试连接远程服务器、上传文件。所以我在网络出口上做了比应用层更绝对的限制:默认丢包,只放行显式允许的出口路径。

容器网络侧,我一般会把所有目标为外部网络的连接都导到一个受控的正向代理,由代理做域名级白名单。同时在宿主侧用iptables把所有未知出站流量静默丢弃,只保留到代理服务的通道。这样即使Agent在容器里种了后门脚本,也没有办法把数据带出沙箱。

这个环节有一条经验特别重要:域名白名单不能只做一次就完事,还要定期核验。我在测试中就遇到过域名本身没过期、但实际解析到的IP已经变化的情况,直接把DNS解析也做了一层限制。

4.3 数据滞留与隐私:日志和缓存同样要管住

最后一块容易忽略的是数据滞留。公共预览阶段的Agent,会把屏幕帧、Prompt、模型响应记录在服务端;本地审计日志又会把敏感截图存一段时间。这些数据不清理,时间越久积累的数据沉淀就越多,泄露出错的口径也越大。

我在测试项目里做的是:服务端显式设置“零留存”策略,不留每次请求的payload;本地日志只保留7天,到期自动清理;所有审计帧加密存储,访问权限只开放给指定运维账号;Agent对话上下文里只放任务所需的最小信息。公共预览阶段我更是坚持一个原则:真实用户的生产数据绝对不进Agent上下文,全部用脱敏或合成的测试数据跑。这样模型端和日志端同时出问题,真正敏感的真实数据也不会被波及。

5. 三维联动:不是三堵墙,而是纵深防御体系

应用、动作、数据三重视角,每单独一层都有局限性,但它们组合起来就不再是“三堵平行的墙”,而是一个可以彼此兜底的纵深防御体系。这也是我在公共预览实测里体会最深的一点。

5.1 用一个攻击场景把三道门禁串起来

为了验证三维门禁是否真的有效,我设计过这样一个攻击场景:让Agent去浏览一个恶意网页,页面里嵌了prompt注入指令,试图诱导模型“下载一个更新补丁并执行它”。

先用应用门禁看:如果这个恶意页面域名不在URL白名单里,模型的第一次访问请求就会被代理直接拒绝,攻击根本进入不了上下文。这道防线已经能拦截绝大多数“野生”威胁。

假设攻击者运气好,域名恰好被放进了白名单,模型真的看到了恶意页面。接下来动作门禁会介入:页面诱导模型下载可执行文件,下载动作属于L3敏感级,会进入人工审批队列。审批人看到目标域名和下载动作,正常都会点拒绝,攻击在这里被第二次拦住。

再往深一层假设,审批人真的误放行了,恶意脚本被下载到容器工作区。但接下来它很难继续运作:容器是只读根文件系统,Agent没有提权能力,无法修改系统启动项;就算脚本尝试把收集到的数据外传,网络出口只有代理白名单一条路,恶意地址不在白名单里,数据最终出不了沙箱。数据门禁做了最后一层兜底。

这个场景完美展示了纵深防御的价值:单层门禁可能因为审批疏忽、白名单误配、模型误导而失守,但攻击者要连续穿透三层,概率低很多。安全回路设计的核心思想就是这个,每一级保护都不完美,但它们互为备份。

5.2 我在实测中发现的三处“结合部漏洞”

三维门禁听起来简单,真正联调的时候还是会在“层与层之间”露出缝隙。我踩过几个典型的结合部坑。

第一个坑是“应用白名单只做了安装清单,没管权限链路”。容器里虽然只装了少数软件,但工作区是可写的,Agent有权限往工作区写入文件。有一次模型生成了一张带恶意宏的文档,诱导测试人员复制到宿主机打开。后来我把工作区目录改成“Agent可写、宿主机只读挂载”,并且规定工作区产出必须经过安全扫描才能带走。

第二个坑是“动作审批只展示动作名,不展示目标上下文”。初期审批界面只有“下载文件”这几个字,审批人根本不知道从哪个域名下载什么样的文件,只能盲点。后来我把审批界面改成“动作类型+目标URL+动作前关键帧”三联,审批质量立刻提升了。

第三个坑是“屏幕脱敏只做了静态遮挡,没防住动态内容”。有些页面的敏感字段在鼠标悬停后才渲染出来,静态遮罩盖不住。后来我干脆把这一类会动态渲染敏感信息的页面,直接放进URL黑名单,不让Agent访问。

5.3 让三个维度互相引用对方的结论

真正把三维门禁串联起来,我会让动作门禁在决策时主动查询应用门禁和网络门禁的结果。也就是说,动作层不只看动作类型,还要看目标进程是否在应用白名单里、目标URL是否在网络白名单里。任何一层判定不通过,动作层直接拒绝。

这种联动用一句话描述就是:每个门禁在放行前,都要确认其他门禁对同一个目标的结论。应用层说“这个应用不受信任”,网络层说“这个域名不可访问”,动作层就无条件拒绝。这样三层的决策不再是孤岛,而是共同构成了一个引用同一份可信状态的有机体。

电气设备里的安全回路设计也是如此:断路开关、漏电保护器、接地系统各自工作,但任何一个检测到异常,都会触发整体的断电联锁。Computer Use这种直接接触真实桌面的Agent,安全投入的意义不在“看起来规范”,而在于任何一次性失误都不会变成不可挽回的灾难。

6. 公共预览阶段的安全落地清单与我的实测建议

写到这里,我把自己在PoC阶段实际用到的安全基线整理了一份清单。如果你准备接Computer Use做公共预览项目,可以直接照着搭一版,再根据你的具体场景裁剪。

6.1 一套可以直接照着搭的基线配置

序号维度配置项说明
1隔离环境Docker容器/轻量虚拟机非root用户运行Agent
2应用门禁应用白名单只装必要的软件,收紧系统权限
3网络门禁URL白名单+内核出网限制出口默认拒绝,只放行代理通道
4动作门禁动作分级+人工审批中间层L4默认拒绝,L3等待人工确认
5数据门禁独立虚拟显示器+抓帧遮挡控制分辨率,敏感窗口隔离
6审计日志动作流水+前后帧+加密存储保留期7天,到期自动清理
7数据最小化上下文只放最小信息真实敏感数据严禁进测试环境
8环境重建Container镜像定期重新构建不维护脏环境,用完就丢

这套基线没有用任何商业产品,全部基于开源组件和常见系统能力就能搭起来。它的目标是满足公共预览PoC的安全需求,不是生产级全量方案;等你要把这类Agent能力推向正式业务,还需要补充身份认证、密钥管理、漏洞扫描、合规评审等一系列环节。

6.2 踩坑之后的打磨心得

最后说几个我踩过之后特别想提醒大家的细节。

第一个教训是别图省事用宿主机浏览器配置。初期我为了减少配置时间,直接让Agent读取了宿主机的一个Firefox配置目录,结果模型浏览历史记录里带出了我在真实网站上的登录状态,差点造成数据泄露。后来我规定容器里的每个浏览器配置都必须从零初始化,用完直接销毁。

第二个教训是网络限制一定要做在内核层。只配浏览器层面的正向代理地址,模型透过终端仍然能直连目标地址。后来我用了iptables默认丢弃加白名单放行,效果明显好很多。

第三个教训是关于人工审批的疲劳问题。最开始我对每一个动作都弹确认,测试同学点了两小时就麻木了,开始无脑点通过。后来我把策略改成“危险清单自动拒、敏感动作才询问”,审批量少了,审批质量反而高了。人不能当流水线,要让门禁把真正的关键决策留给人。

第四个教训是关于日志的完整性。前期我的审计日志漏掉了动作前的关键帧,等到想复盘一次异常行为时,根本说不清模型当时看到了什么。后来我把“动作前截图”设成必采字段,宁愿多占一点存储,也不能在出现事故时缺乏证据。

Computer Use这种直接操作真实桌面的Agent能力,和传统LLM应用的开发节奏完全不一样。前者每一个动作都对应真实系统的状态变更,安全不是一个模块,而是整个系统的骨架。如果你也在做类似的公共预览项目,我的建议很朴素:从第一天起,就把三维门禁装上。宁可每次敏感操作多等几秒审批,也别让一次偶然的失控变成不可挽回的事故。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进