在现代 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' 标头以阻止嵌入端点。
  • 通过以下方式在隔离的子网域上对用户内容进行沙盒化:
    • 在独立的子网域上提供用户内容(例如,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 事件处理脚本并呈现所收到的任何内容。
  • 为此,该产品会创建一个 iframe 或弹出式窗口到 $RANDOM_VALUE.exampleusercontent.com/shim,并使用 postMessage 将不受信任的内容发送到 shim 进行呈现。
  • 呈现的内容会转换为 Blob,并呈现在沙盒化 iframe 内。

与经典的沙盒域方法相比,这样可以确保所有内容都完全隔离在一个唯一的网站上。此外,通过让主应用处理检索要呈现的数据,便不再需要使用功能网址。

总结

通过结合使用这两种解决方案,您可以从 googleusercontent.com 等经典沙盒网域迁移到与第三方 Cookie 拦截兼容的更安全解决方案。Google 已经迁移了许多产品来使用这些解决方案,并计划明年进行更多迁移。