在现代 Web 应用中安全地托管用户数据

David Dworken
David Dworken

许多 Web 应用都需要显示由用户控制的内容。这可能简单到提供用户上传的图片(例如个人资料照片),也可能复杂到呈现用户控制的 HTML(例如网页开发教程)。这一直以来都很难安全完成,因此我们一直在努力寻找简单又安全的解决方案,这些解决方案可应用于大多数类型的 Web 应用。

用于隔离不受信任的内容的传统解决方案

安全传送由用户控制的内容的传统解决方案是使用所谓的沙盒域。基本思路是,如果应用的主域是 example.com,您可以在 exampleusercontent.com 上提供所有不受信任的内容。由于这两个网域是跨网站网域,因此 exampleusercontent.com 上的任何恶意内容都不会影响 example.com
这种方法可用于安全地传送各种不受信任的内容,包括图片、下载内容和 HTML。虽然看起来不一定有必要为图片或下载内容使用此属性,但这样做有助于避免内容嗅探风险,尤其是在旧版浏览器中。
沙盒域在整个行业中得到广泛应用,并且长期发挥良好的效果。但是,它们有两个主要缺点:

  • 应用通常需要将内容访问权限限制为单个用户,这需要实现身份验证和授权。由于沙盒域会有意不与主应用程序域共享 Cookie,因此这样做非常困难。要支持身份验证,网站要么依靠功能网址,要么必须为沙盒网域设置单独的身份验证 Cookie。第二种方法在现代网络中尤其容易出现问题,因为许多浏览器在默认情况下都限制了跨网站 Cookie。
  • 虽然用户内容与主网站隔离开来,但并不会与其他用户内容隔离开来。这会带来恶意用户内容攻击沙盒网域上其他数据(例如通过读取同源数据)的风险。

另外值得注意的是,由于资源被明确划分到一个隔离的域,因此沙盒域有助于降低网络钓鱼风险。

用于提供用户内容的现代解决方案

随着网络的发展,现在有更简单、更安全的方法来提供不受信任的内容。具体采用的方法有很多,因此我们将概述 Google 目前广泛使用的两种解决方案。

方法 1:提供不活跃的用户内容

如果网站只需要提供非活动用户内容(即除 HTML 或 JavaScript 以外的内容,例如图像和下载内容),那么现在无需使用隔离的沙盒域即可安全地完成此操作。有两个关键步骤:

  • 请务必将 Content-Type 标头设置为广为人知的 MIME 类型,这种类型受所有浏览器支持,并且保证不包含有效内容(如果不确定,application/octet-stream 是安全的选择)。
  • 此外,请务必设置以下响应标头,以确保浏览器将相关响应完全隔离开来。
响应标头 Purpose

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 限制提供了多层保护来防范浏览器 bug。总体而言,根据上述设置,我们高度确信以这种方式提供的响应不会导致注入漏洞或隔离漏洞。

深度防御

尽管上述解决方案通常能够有效抵御 XSS,但您还可以应用多种额外的安全强化措施,以提供额外的安全防护:

  • 设置 X-Content-Security-Policy: sandbox 标头以与 IE11 兼容。
  • 设置 Content-Security-Policy: frame-ancestors 'none' 标头以阻止嵌入端点。
  • 通过以下方式在隔离的子网域上沙盒化用户内容: <ph type="x-smartling-placeholder">
      </ph>
    • 在独立的子网域上提供用户内容(例如,Google 使用 product.usercontent.google.com 等网域)。
    • 设置 Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp 以启用跨域隔离

方法 2:提供活跃用户内容

安全传送主动内容(例如 HTML 或 SVG 图片)也可以实现,而且不存在传统沙盒域方法的弱点。
最简单的方法是利用 Content-Security-Policy: sandbox 标头,让浏览器隔离响应。虽然目前并非所有的网络浏览器都对沙盒文档实现进程隔离,但对浏览器进程模型的持续优化可能会改善沙盒内容与嵌入应用的分离。如果 SpectreJS渲染器入侵攻击不在威胁模型范围之内,那么使用 CSP 沙盒可能就足够了。
Google 开发了一种解决方案,可以通过对沙盒域的概念进行现代化改造来完全隔离不受信任的主动内容。其核心思想是:

  • 创建一个添加到公共后缀列表中的新沙盒网域。例如,通过向 PSL 添加 exampleusercontent.com,您可以确保 foo.exampleusercontent.combar.exampleusercontent.com 是跨网站,从而彼此完全隔离。
  • *.exampleusercontent.com/shim 匹配的网址都会路由到一个静态填充码文件。此填充码文件包含一个简短的 HTML 和 JavaScript 代码段,用于监听 message 事件处理脚本并呈现其收到的任何内容。
  • 为此,该产品会创建一个指向 $RANDOM_VALUE.exampleusercontent.com/shim 的 iframe 或弹出式窗口,并使用 postMessage 将不受信任的内容发送到 shim 进行呈现。
  • 呈现的内容将转换为 Blob 并在沙盒化 iframe 内呈现。

与传统的沙盒域方法相比,这可确保所有内容都完全隔离到一个唯一的网站上。而且,通过让主应用程序处理检索要呈现的数据,不再需要使用功能网址。

总结

通过结合使用这两种解决方案,您可以弃用 googleusercontent.com 等传统沙盒网域,迁移到可与第三方 Cookie 屏蔽功能兼容的更加安全的解决方案。在 Google,我们已经为使用这些解决方案的许多产品进行了迁移,并计划在明年进行更多迁移。