Developers don't understand CORS (2019)
TL;DR · AI 摘要
CORS机制被误解导致Zoom出现安全漏洞,正确配置可避免此类问题。
核心要点
- Chrome浏览器实际上会遵循localhost服务器的CORS策略。
- Zoom使用图片尺寸传递状态码以绕过CORS,导致安全漏洞。
- 正确的做法是设置Access-Control-Allow-Origin和Content-Security-Policy头。
结构提纲
按章节快速跳转。
- §引言
作者通过与不同水平的开发者合作,发现CORS机制被广泛误解。
Zoom使用图片尺寸传递状态码以绕过CORS,导致安全漏洞。
Chrome浏览器实际上会遵循localhost服务器的CORS策略。
- ›安全建议
正确的做法是设置Access-Control-Allow-Origin和Content-Security-Policy头。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- CORS误解与Zoom漏洞
- CORS机制
- 浏览器遵循CORS策略
- 绕过CORS的错误方法
- Zoom漏洞
- 使用图片尺寸传递状态码
- 导致安全漏洞
- 安全建议
- 设置Access-Control-Allow-Origin
- 设置Content-Security-Policy
金句 / Highlights
值得收藏与分享的关键句。
Chrome does respect CORS headers for localhost webservers.
This will ensure that only Javascript running on the zoom.us domain can talk to the localhost webserver.
By doing this, they opened Zoom up to a big vulnerability because not only can the Zoom website trigger operations in the native client and access the response, but every other website on the internet
开发者并不理解 CORS - Chris Foster
Chris Foster
博客 -
简介
-
演讲
开发者并不理解 CORS
2019 年 7 月 10 日 — Chris Foster
全栈咨询工作最棒的地方之一就是我可以与来自不同公司、不同规模和不同行业的众多开发者合作,他们拥有不同的技能水平。这让我有机会看到一些普遍存在的挑战。最近一个看起来很常见且相关的问题是:太多网页开发者并不理解 CORS 的工作原理。
内容分隔器
指出这一点似乎特别及时,因为最近 Zoom 存在一个漏洞。安全研究员 Jonathan Leitschuh 发现 Zoom 在本地机器上运行了一个监听 http://localhost:19421 的网页服务器。当你加载一个 Zoom 链接时,Zoom 的网站会向本地网页服务器发送请求,并让它打开本地的 Zoom 应用。这篇文章值得一读,但其中一些内容引起了我的注意:
我还发现,这个页面并没有使用常规的 AJAX 请求,而是从本地运行的 Zoom 网页服务器加载了一张图片。图片的不同尺寸决定了服务器返回的错误/状态码。你可以在这里看到这种 case-switch 逻辑。我问自己一个问题,为什么这个网页服务器会将数据编码在图片文件的尺寸中?原因是为了绕过跨域资源共享(CORS)。出于非常明确的原因,浏览器会明确忽略运行在 localhost 上的服务器的 CORS 策略。
最后一句话是错误的 — Chrome 会尊重运行在 localhost 上的网页服务器的 CORS 头信息。如果你是网页开发者,你可能在使用 Create React App 时遇到过这种情况,前端应用运行在一个端口,而后端 API 运行在另一个端口。你的应用会向 localhost 发起跨域请求,所有浏览器都支持这种操作。
这让我觉得 Zoom 可能需要快速推出这个功能,但并不理解 CORS。他们无法在浏览器不允许的情况下进行 AJAX 请求。于是,他们使用了这种图片技巧来绕过 CORS。通过这样做,他们让 Zoom 面临一个重大漏洞,因为不仅 Zoom 网站可以触发本地客户端的操作并访问响应,互联网上的任何其他网站也可以做到这一点。
那么,这个功能的安全实现方式应该是什么样呢?监听在 localhost:19421 上的网页服务器应该实现一个 REST API,并设置 Access-Control-Allow-Origin 头信息,其值为 https://zoom.us。这将确保只有运行在 zoom.us 域名上的 JavaScript 才能与本地网页服务器通信。此外,为了防止页面在后台自动打开 Zoom 会议,zoom.us 应该设置一个内容安全策略(Content Security Policy)头信息,以阻止在 iframe 中渲染。
这仍然存在一个漏洞,即任何页面都可以将你的浏览器重定向到一个你没有预期的 zoom.us 会议链接,但这是 Zoom 做出的用户体验决策,而不是软件漏洞。我个人认为,这种做法也是错误的。他们提到他们希望通过直接打开应用程序来改善用户体验,但好的用户体验设计的一个规则是,你的软件必须具有可预测性。
如果我在点击一个链接,我期望它不会突然让我将摄像头和麦克风权限提供给我不认识的人。Zoom 的做法打破了这种预期。即使他们出于用户体验的原因不希望使用内置浏览器弹窗,也请将这个弹窗放在应用内!Google Meet 就做得很好:
我不想偏离本文对 CORS 的关注。无论从用户体验的角度如何争论,运行一个本地主机上的 Web 服务器本身就是一个风险性很高的行为。它绝对不应该为互联网上的每一个网站提供访问特权功能(如安装软件)的权限。CORS 使你能够安全地实现这一点——不要绕过它!
我无法确定是否是由于对 CORS 的理解不足,导致 Zoom 以这种方式实现该功能。然而,我和一些人讨论过,我们都没有找到任何合理的理由来支持他们目前的实现方式。在 Reddit 上,lerunicorn 发现并建议说,Firefox 可能会阻止从安全来源到非安全来源的 XHR 请求,这可能解释了他们采用这种做法的动机。然而,当来源是 localhost 的时候,Firefox 是支持这种行为的。另外,原生应用可以生成一个唯一的自签名证书。或者,他们也可以使用浏览器扩展。无论哪种情况,这都不是一个合理的理由,去忽略对来源的过滤。
这不仅仅是 Zoom 的问题。根据我的经验,很多我交谈过的开发者对 CORS 的工作原理理解得并不好。在 Stack Overflow 上也有很多类似的例子。不幸的是,这些例子经常伴随着推荐非常不安全默认设置的页面,比如这个在 express 中的页面,如果直接复制使用,会使你的应用面临风险。其他厂商也曾被发现存在与 Zoom 相同的漏洞。
开发者只是希望他们的代码能正常运行,完全绕过同源策略可能会让代码运行起来,但一旦有人发现你这么做,你就会像 Zoom 现在一样遇到问题。
我看到无论是有经验还是刚入门的开发者,都会对 CORS 产生困惑。是 CORS API 太复杂、太令人困惑,还是我们只需要在 CORS 和 CSP 等问题上对开发者进行更好的教育?我不确定,但目前的做法显然没有奏效。
订阅电子邮件列表
©
Chris Foster
—
chris@fosterelli.co