许多web应用程序需要显示用户控制的内容。这可能是简单的服务用户上传的图像(例如个人资料照片),也可能是复杂的渲染用户用户控制的HTML
(例如网络开发教程)。这一直很难安全地做到,所以我们一直在努力寻找简单,但安全的解决方案,可以应用于大多数类型的web
应用程序。
用于隔离不受信任内容的经典解决方案
安全地提供用户控制的内容的经典解决方案是使用所谓的沙盒。基本思想是,如果您的应用程序的主域是example.com
,您可以将所有不受信任的内容放在exampleusercontent.com
上。由于这两个域是跨域,exampleusercontent.com
上的任何恶意内容都不会影响example.com。
这种方法可用于安全地提供各种不受信任的内容,包括图像、下载和HTML
。虽然对于图像或下载似乎不需要这样做,但这样做有助于避免内容嗅探的风险,特别是在传统浏览器中。
沙盒在整个行业广泛使用并且长期以来一直运作良好。但是,它们有两个主要缺点:
- 应用程序通常需要将内容访问限制为单个用户,这需要实施身份验证和授权。由于沙箱域有意不与主应用程序域共享
cookie
,因此这样做非常难以安全地实现。为了支持身份验证,站点必须依赖于功能URL
,或者它们必须为沙箱域设置单独的身份验证cookie
。在现代网络中,许多浏览器默认限制跨站点cookie
,因此第二种方法尤其有问题。 - 虽然用户内容与主站点隔离,但它并不与其他用户内容隔离。这会导致恶意用户内容攻击沙盒上的其他数据的风险(例如,通过读取同源数据)。
值得注意的是,沙盒有助于缓解网络钓鱼风险,因为资源明确分割到一个隔离的域上。
##用于提供用户内容的现代解决方案
随着时间的推移,网络已经发展,现在有更简单、更安全的方法来提供不受信任的内容。这里有许多不同的方法,因此我们将概述两种当前在Google
广泛使用的解决方案。
方法1:提供不活动的用户内容
如果一个站点只需要提供不活动的用户内容(即不是HTML
或JavaScript
的内容,例如图像和下载),那么现在可以安全地在不隔离的沙盒的情况下执行。有两个关键步骤:
- 始终将
Content-Type
标头设置为所有浏览器都支持的并且保证不包含活动内容的众所周知的MIME
类型(application/octet-stream
是一个安全的选择)。 - 此外,始终设置下面的响应标头以确保浏览器完全隔离响应。
响应标头 | 目的 |
---|---|
X-Content-Type-Options:nosniff | 防止内容嗅探 |
Content-Disposition:attachment; filename=”download” | 触发下载而不是渲染 |
Content-Security-Policy:sandbox | 将内容隔离,就像它是在单独的域上提供的一样 |
Content-Security-Policy:default-src ‘none’ | 禁用JavaScript执行(以及任何子资源的包含 |
Cross-Origin-Resource-Policy:same-site | 防止页面被跨站点包含 |
这些标头的组合确保响应只能由您的应用程序作为子资源加载,或者被用户下载为文件。此外,标头通过CSP
沙箱标头和default-src
限制提供了多层保护,以防止浏览器漏洞。总的来说,上述设置提供了高度的保障,以确保以这种方式提供的响应不会导致注入或隔离漏洞。
深度防御
虽然上述解决方案代表了一种通常足够的防御措施防止XSS,但是您可以应用一些额外的加固措施来提供额外的安全层:
- 为了与
IE11
兼容,请设置X-Content-Security-Policy:sandbox
标头。 - 设置Content-Security-Policy:
frame-ancestors 'none'
标头以阻止嵌入该端点。 - 通过以下方式在隔离的子域上对用户内容进行沙箱处理:
- 在隔离的子域上提供用户内容(例如,
Google
使用诸如product.usercontent.google.com
之类的域)。 - 设置Cross-Origin-Opener-Policy:
same-origin
和Cross-Origin-Embedder-Policy:require-corp
以启用跨域隔离。
- 在隔离的子域上提供用户内容(例如,
方法2:服务活跃用户内容
安全地提供活跃内容(例如HTML
或SVG
图像)也可以避免经典沙箱域方法的弱点。
最简单的选择是利用Content-Security-Policy:sandbox
标头告诉浏览器隔离响应。虽然当前并非所有网络浏览器都实现了沙箱文档的进程隔离,但不断完善的浏览器进程模型很可能会改善沙箱内容与嵌入应用程序之间的分离。如果SpectreJS
和渲染器攻击不在您的威胁模型之内,那么使用CSP
沙箱很可能是足够的解决方案。
在Google
,我们开发了一种可以通过现代化沙箱域的概念完全隔离不受信任的活跃内容的解决方案。核心思想是:
- 创建一个新的沙箱域,将其添加到公共后缀列表中。例如,通过将
exampleusercontent.com
添加到PSL
,您可以确保foo.exampleusercontent.com
和bar.exampleusercontent.com
是跨站点的,因此彼此完全隔离。 - 与
*.exampleusercontent.com/shim
匹配的URL
全部路由到一个静态的shim
文件。这个shim
文件包含一个简短的HTML
和JavaScript
片段,监听message
事件处理程序并渲染接收到的任何内容。 - 为了使用这个方法,产品创建一个
iframe
或一个弹出窗口到$RANDOM_VALUE.exampleusercontent.com/shim
,并使用postMessage
将不受信任的内容发送到shim
进行渲染。 - 渲染的内容被转换为
Blob
并在一个沙箱iframe
中呈现。
与经典的沙箱域方法相比,这确保了所有内容完全隔离在一个唯一的站点上。而且,通过让主应用程序处理要呈现的数据的检索,不再需要使用功能URL
。
结论
这两种解决方案的结合使得可以迁移到更安全的解决方案,这些解决方案与第三方Cookie
阻止兼容,从而摆脱了经典的沙箱域(如googleusercontent.com
)。在Google
,我们已经迁移了许多产品以使用这些解决方案,并计划在未来一年进行更多的迁移。