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

David Dworken
David Dworken

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

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

  • 应用通常需要限制单个用户对内容的访问权限,这需要实现身份验证和授权。由于沙盒网域会刻意不与主应用网域共享 Cookie,因此很难安全地执行此操作。如需支持身份验证,网站必须依赖于capability 网址,或者必须为沙盒网域设置单独的身份验证 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' 标头以阻止嵌入端点。
  • 通过以下方式在隔离的子网域中沙盒化用户内容:
    • 在隔离的子网域上提供用户内容(例如,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 中进行渲染。

与传统的沙盒网域方法相比,这种方法可确保所有内容都完全隔离在一个唯一的网站上。此外,通过让主要应用处理要呈现的数据的检索,您无需再使用 capability 网址。

总结

这两种解决方案相辅相成,可让您从 googleusercontent.com 等传统沙盒网域迁移到与第三方 Cookie 屏蔽功能兼容的更安全的解决方案。Google 已经将许多产品迁移到了这些解决方案,并计划在明年进行更多迁移。