Files
Nexus/docs/audit/2026-06-12-remote-browser-worker.md
T
Nexus Agent dbb6bc4a55 fix(browser): 门控 — Worker TLS verify 可配置、审计清单与 shell LF
默认校验 Worker HTTPS 证书;审计文件补全变更清单;docker-entrypoint.sh 改为 LF。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 06:50:06 +08:00

7.2 KiB
Raw Blame History

审计 — 远程浏览器(Playwright Worker

Changelog: docs/changelog/2026-06-12-remote-browser-worker-deploy.md
设计 SSOT: docs/design/specs/2026-06-11-server-browser-chromium-design.md
范围: 2026-06-12 新增/恢复的浏览器 Worker 全链路(未含 web/app 构建产物)

范围(文件清单)

文件 职责
browser-worker/Dockerfile Worker 镜像
browser-worker/docker-compose.yml Worker 编排
browser-worker/docker-entrypoint.sh headed + Xvfb 入口
browser-worker/requirements.txt Worker 依赖
browser-worker/config.py Worker 环境配置
browser-worker/main.py Worker HTTP/WS API
browser-worker/session_manager.py Playwright + screencast
browser-worker/browser_stealth.py 反自动化 / 验证码辅助
browser-worker/browser_url_safe.py SSRFWorker 侧)
server/main.py 注册 browser 路由
server/config.py BROWSER_* 与 TLS verify
server/api/settings.py 设置项默认值(push_complete_sound
server/api/browser_session.py 桥接 REST + /ws/browser
server/api/browser.py UI 状态 /api/browser/state
server/infrastructure/browser/__init__.py 包入口
server/infrastructure/browser/worker_client.py 连 Worker(可配置 TLS verify
server/infrastructure/browser/session_registry.py 内存会话归属
server/utils/browser_url_safe.py SSRF(桥接层)
server/utils/browser_ui_state.py 状态解析
frontend/src/App.vue 顶栏 + 固定 GlobalBrowserPanel
frontend/src/router/index.ts /browser 路由
frontend/src/pages/ServersPage.vue 服务器页打开浏览器
frontend/src/pages/BrowserPage.vue 独立浏览器页(兼容)
frontend/src/components/GlobalBrowserPanel.vue 固定顶栏下整窗
frontend/src/composables/useGlobalBrowser.ts 全局标签/状态
frontend/src/composables/useRemoteBrowserSession.ts canvas + WS
tests/test_browser_url_safe.py SSRF 单测
tests/test_browser_ui_state.py UI 状态单测
tests/test_settings_sound_defaults.py 设置默认音单测

API / WS 入口表

方法 路径 鉴权
GET /api/browser/status JWT get_current_admin
POST /api/browser/sessions JWT
DELETE /api/browser/sessions/{id} JWT + 会话归属
GET/PUT /api/browser/state JWT + admin_ui_preferences
WS /ws/browser/{session_id}?token= JWT + 会话归属
GET Worker /health
POST/DELETE Worker /v1/sessions* X-Nexus-Worker-Key
WS Worker /v1/sessions/{id}/stream Worker KeyHeader

Step 3 规则扫描

ID 规则 结论
H1 SSRF / 私网访问 RISK — 见 F1、F2
H2 鉴权 / IDOR PASS — WS 校验 admin_id
H3 密钥不落库/不入 git PASS — .env / 持久卷
H4 无静默吞错 RISK — 见 F4
H5 Worker 暴露面 RISK — 见 F3
H6 多 worker 一致性 RISK — 见 F5
H7 UI 状态 URL 白名单 PASS — browser_ui_state
H8 设计符合度(redirect GAP — 见 F1

Closure(全表)

ID 判定 严重度 位置 说明
H1 FINDING P1 browser-worker/session_manager.py NAVIGATE 后 F1 页内点击/重定向未逐跳 SSRF 校验;设计文档要求 redirect 后重验
H1 FINDING P1 Worker 部署 ufw F3 装机时额外 ufw allow 8443/tcp(全网),削弱「仅主站 IP」隔离
H2 SAFE browser_session.py:125-127 session_id 必须属于当前 admin.id
H2 SAFE browser_session.py:95-104 桥接层 NAVIGATE 二次校验
H3 SAFE worker_client.py, Worker .env WORKER_SECRET 仅环境变量
H4 FINDING P2 worker_client.py:67-68 F4 delete_worker_session 网络失败仅 warning,可能残留 Worker 会话
H5 FINDING P2 browser-worker/main.py:45-51 /health 无认证;若 8443 对公网可见会泄露会话数
H6 FINDING P2 session_registry.py F5 内存注册表不跨 uvicorn worker;多进程下 WS 可能 4403
H7 SAFE browser_ui_state.py http(s) only,无凭据 URL
H8 SAFE screencast 热修 cdp.on("Page.screencastFrame") 已替代错误 API
SAFE browser_session.py:61-64 创建前清理陈旧会话,避免 409
SAFE 前端 不再 iframecanvas 远程渲染
SAFE CUD 审计 首版未记 session 审计,与设计「与 RDP 一致」一致

F1 — 重定向 / 页内导航 SSRF 缺口(P1)

validate_navigation_url 仅在显式 NAVIGATE 消息时调用。用户在远程 Chromium 内点击链接302 跳转http://10.x / metadata 时,Playwright 会照常请求,不经过校验。

建议page.on("framenavigated") / response 监听,每跳对 page.url 调用 validate_navigation_url;失败则 page.close()about:blank 并 WS ERROR

F3 — Worker 防火墙过宽(P1

部署日志显示除 from 20.24.218.235 外还有 ufw allow 8443/tcpAnywhere)。攻击者若猜到 WORKER_SECRET(或暴力)可直连 Worker。

建议:删除全网 8443 规则,仅保留主站源 IP;/health 改内网或加 Key。

F4 — Worker 删除失败(P2

delete_worker_session 异常时静默;依赖 Worker 空闲超时清理。

F5 — 多 uvicorn workerP2

session_registry 进程内字典;生产若 --workers>1 可能 POST 在 worker A、WS 落到 worker B。当前 Docker 单进程可接受;扩 worker 时需 Redis 注册表。

外部输入 → Sink

输入 Sink 缓解
NAVIGATE.url Playwright goto 双端 validate_navigation_url
页内链接点击 Playwright 导航 未缓解(F1
WS MOUSE/KEY Chromium 仅已认证管理员
browser/state tabs URL MySQL JSON _is_safe_url 过滤
Worker Key Worker API 常量时间比较(!=

DoD

状态
pytest tests/test_browser_url_safe.py tests/test_browser_ui_state.py 7 passed(生产机);本地沙箱 DNS 失败属环境限制
npm run type-check 已通过(实现日)
生产 GET /api/browser/status enabled:true, worker_ok:true
生产 screencast 冒烟 SCREENCAST_OK ~4KB JPEG
changelog docs/changelog/2026-06-12-remote-browser-worker-deploy.md

总结

级别 数量 处置建议
P0 0
P1 2 F1 redirect/页内 SSRFF3 收紧 ufw
P2 3 F4/F5 可排期;/health 加固

合并结论:功能链路可用,鉴权与显式 NAVIGATE SSRF 达标;未达设计文档「每跳 redirect 校验」,且 Worker 8443 应对公网收口。建议先修 F3(运维),再实现 F1(代码)后复审计。

验证命令

venv/bin/pytest tests/test_browser_url_safe.py tests/test_browser_ui_state.py -q
cd frontend && npm run type-check
# 生产
curl -H "Authorization: Bearer $TOKEN" https://api.synaglobal.vip/api/browser/status