2019 年 Zoom 那个漏洞,最刺眼的细节不是客户端在本机起了一个 Web Server。
真正反常的是:Zoom 为了让网页和本地服务通信,用了一张“图片”的宽高来传状态。
Zoom 客户端会监听 http://localhost:19421。用户点击会议链接后,网页请求这个本地服务,再唤起原生客户端。正常做法应该走受浏览器安全模型约束的接口。Zoom 当时的方案更绕:让本地服务返回图片,用图片尺寸编码状态码,避开 AJAX 读取时会遇到的 CORS 限制。
这不是单纯的代码失误。它更像一次工程激励的暴露:功能要顺,体验要无感,边界就被当成麻烦。
这个漏洞真正绕过了什么
把事实压短看:
| 问题 | 事实 | 影响 |
|---|---|---|
| 本地服务 | Zoom 监听 http://localhost:19421 | 浏览器网页可以碰到本机客户端能力 |
| 通信方式 | 用图片宽高传状态 | 避开 AJAX/CORS 的跨源读取限制 |
| 常见误解 | 以为 localhost 天然不受 CORS 管 | Chrome 等浏览器会尊重 localhost 服务返回的 CORS header |
| 风险边界 | 不只 zoom.us 能触发这条链路 | 任意网站都有机会把用户带进唤起客户端的流程 |
这里最容易讲错。
CORS 不是服务器鉴权。它不能证明用户是谁,也不能替代登录态、token、权限校验。
CORS 主要限制的是:浏览器里的跨源读取。很多跨源请求本来就可能发出去,关键在于浏览器是否允许调用方读到响应。把 CORS 当成“服务器安全认证机制”,是另一种误解。
但它也不是可有可无的前端报错。
如果 Zoom 的本地服务只允许:
Access-Control-Allow-Origin: https://zoom.us
那至少能把读取本地特权接口响应的网页限制在 Zoom 自家域名下,而不是让全互联网都能轻松参与这条链路。
再加一层,Zoom 页面可以通过 CSP 禁止被 iframe 渲染,降低网页在后台自动打开会议的风险。这个措施不能解决一切。任意页面仍然可以跳转到 zoom.us 的会议链接。那一部分更接近产品体验决策,而不只是安全漏洞。
Google Meet 的会前确认体验就提供了一个对照。点链接后,用户能看到要进哪个会议、摄像头和麦克风是什么状态。它不炫技,但给了用户可预测性。
可预测性本身就是安全体验的一部分。
受影响的不是 Zoom 一家
我不愿意断言 Zoom 当时一定是不懂 CORS。原作者也说过,无法确认团队动机。
真实情况可能更复杂:兼容性、上线压力、历史包袱、临时方案变成正式架构。工程现场常常就是这样。没人宣布要破坏安全边界,但每个人都在为“先跑起来”让一步。
问题是,边界一旦让出去,漏洞迟早会来收账。
对不同角色,这件事的提醒不一样:
| 对象 | 该调整什么 | 不该怎么做 |
|---|---|---|
| Web 前后端开发者 | 遇到 CORS 报错时,先确认 origin、credential、读取权限和接口意图 | 直接把 Access-Control-Allow-Origin 改成 * |
| 技术负责人 | 把 localhost 服务、本地助手、客户端唤起流程纳入安全评审 | 只验收“链接能打开、客户端能拉起” |
| 安全工程师 | 重点看本地特权接口是否限制来源、是否需要用户确认、是否能被 iframe/跳转链滥用 | 只扫远程 API,忽略本机端口 |
这类风险不只属于会议软件。
很多本地助手、开发工具、硬件管理器、浏览器扩展配套服务,都喜欢在 localhost 起一个服务,让网页调用本机能力。这个模式本身不是原罪。问题在于,localhost 不是魔法安全区。
它只是用户机器上的一扇门。
门后如果连着摄像头、麦克风、安装器、客户端唤起、本地状态读取,就不能靠“正常网站才会访问”来防守。
Stack Overflow 和各类框架示例还放大了这个问题。很多答案教你用 * 解决 CORS。很多示例代码默认只追求请求成功。复制一下,页面不红了,工单关闭了。
“天下熙熙,皆为利来。”放到工程组织里,就是指标奖励交付,很少奖励边界清楚。
理解 CORS、CSP、origin、credential,短期看不到功能增量。绕过去,马上有结果。组织会自然偏向后者。
接下来要看的不是补丁,而是默认值
安全实现当然要从具体动作开始。
限制 Access-Control-Allow-Origin。不要把本地特权接口暴露给任意网页。该有用户确认的地方,就别为了无感体验偷偷跳过。涉及 iframe、自动唤起、后台打开会议的流程,也要配合 CSP 和产品层确认。
但我更在意默认值。
一个团队如果默认把浏览器同源策略看成障碍,就会不断发明捷径:图片尺寸传状态、JSONP 式回魂、全域名放行、临时白名单永不下线。
这些方案都很像“聪明工程”。问题是,安全事故常常就长在这种聪明里。
接下来判断类似产品,别只看它有没有本地服务。更要看三件事:
- 本地服务是否只信任明确来源,而不是默认信任所有网页。
- 高权限动作是否需要用户可见确认,而不是一跳到底。
- 示例代码、SDK、文档默认值是否安全,而不是把风险留给使用者自己补。
这里有现实约束。浏览器安全模型不算好懂,CORS 的开发体验也不优雅。本地客户端和网页之间的打通,确实常常背着兼容性压力。
但复杂不是绕开的理由。
越靠近摄像头、麦克风、安装器、本机客户端这些高权限能力,越不能把用户的确认感当成体验噪音。产品越想无感,安全越需要有感。
Zoom 这件事的价值,不是多年后翻旧账。它提醒的是下一次。
当你再看到 CORS 报错,别急着把 * 贴上去。那不是浏览器刁难你。那可能是它最后一次替你挡住事故。
