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

David Dworken
David Dworken

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

用于隔离不受信任内容的经典解决方案

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

  • 应用通常需要限制内容访问权限,仅允许单个用户访问,这就需要实现身份验证和授权。由于沙盒网域有意不与主应用网域共享 Cookie,因此很难安全地实现此目的。为了支持身份验证,网站必须依赖功能网址,或者必须为沙盒网域设置单独的身份验证 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,但您还可以采取一些额外的安全加固措施来提供额外的安全层:

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

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

此外,还可以安全地提供有效内容(例如 HTML 或 SVG 图片),而不会出现经典沙盒网域方法的缺点。

最简单的方案是利用 Content-Security-Policy: sandbox 标头来告知浏览器隔离响应。虽然并非所有 Web 浏览器都为沙盒文档实现了进程隔离,但对浏览器进程模型的持续改进可能会提高沙盒化内容与嵌入应用的分离程度。如果 SpectreJS渲染器遭入侵攻击不在您的威胁模型中,那么使用 CSP 沙盒可能是一个充分的解决方案。Google 开发了一种解决方案,通过更新沙盒网域的概念,可以完全隔离不受信任的有效内容。核心思想是:

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

与传统的沙盒网域方法相比,此方法可确保所有内容完全隔离在唯一的网站上。并且,由于主应用负责检索要呈现的数据,因此不再需要使用功能网址。

总结

借助这两项解决方案,您可以从 googleusercontent.com 等经典沙盒网域迁移到更安全的解决方案,这些解决方案与第三方 Cookie 屏蔽功能兼容。在 Google,我们已将许多产品迁移到使用这些解决方案,并计划在明年进行更多迁移。