推出 Storage Access API

注意:阅读有关该技术改进的内容,请参阅最近的博客文章,了解智能跟踪预防和 Storage Access API。

去年六月,我们推出了智能跟踪预防(ITP)。ITP 是一项隐私功能,它能检测哪些域具有跨站点跟踪用户的能力,并对这些域的 Cookie 进行分区或彻底清除其网站数据。

我们收到的关于 ITP 最强烈的开发者反馈是,它需要提供一种方式,让嵌入式跨站点内容能够验证已登录其第一方服务的用户身份。今天,我们很高兴能够以 Storage Access API 的形式提供解决方案。它允许经过身份验证的嵌入内容,同时默认继续保护客户的隐私。

分区 Cookie 和嵌入式内容

假设 socialexample.org 嵌入在多个网站上,以便用户可以使用其 socialexample ID 进行评论或“点赞”内容。ITP 将检测到此类多页面嵌入会使 socialexample.org 具备跨站点跟踪用户的能力,因此会拒绝来自 socialexample.org 的嵌入内容访问其第一方 Cookie,仅提供分区 Cookie。这会破坏用户评论和点赞内容的能力,除非他们在过去 24 小时内曾以第一方站点的身份与 socialexample.org 互动过。(有关分区 Cookie 的具体规则,请参阅原始 ITP 博客文章。)

对于订阅服务中嵌入的第三方支付提供商和嵌入的第三方视频也是如此。一旦 ITP 检测到它们的跟踪能力,就会拒绝它们在 24 小时窗口期外访问第一方 Cookie,并且嵌入内容会将用户视为已注销,即使他们实际上已登录。

我们为了用户隐私做出了权衡。但如果能让用户在实际有兴趣使用第三方 iframe 的情况下,享受在其中登录的便利,同时仍然保护隐私,那就更好了。

解决方案:Storage Access API

解决方案是允许第三方嵌入内容请求访问其第一方 Cookie,当用户与它们交互时。为此,我们创建了 Storage Access API。

Storage Access API 为跨域 iframe 提供了两个新函数——document.hasStorageAccess() 和 document.requestStorageAccess()。它还为嵌入的顶级框架提供了一个新的 iframe sandbox 令牌——“allow-storage-access-by-user-activation”。

在此上下文中,存储访问意味着 iframe 可以访问其第一方 Cookie,即与作为第一方站点时可以访问的 Cookie 相同。请注意,存储访问不会以任何方式放松同源策略。具体来说,这与第三方 iframe 获取嵌入网站的 Cookie 和存储无关,反之亦然。

WebKit 对该 API 的实现目前仅涵盖 Cookie。它不影响其他存储形式(如 IndexedDB 或 LocalStorage)的分区。

检查存储访问

调用 document.hasStorageAccess() 会返回一个 Promise,该 Promise 将解析为一个布尔值,指示文档是否已拥有对其第一方 Cookie 的访问权限。如果 iframe 与顶级框架同源,则该 Promise 返回 true。

var promise = document.hasStorageAccess();
promise.then(
  function (hasAccess) {
    // Boolean hasAccess says whether the document has access or not.
  },
  function (reason) {
    // Promise was rejected for some reason.
  }
);

请求存储访问

在用户手势(如轻触或点击)发生时调用 document.requestStorageAccess() 会返回一个 Promise,如果存储访问被授予,则 Promise 会解析;如果访问被拒绝,则 Promise 会拒绝。如果存储访问被授予,调用 document.hasStorageAccess() 将返回 true。iframe 需要显式调用此 API 的原因是,它能让开发者控制文档 Cookie 更改的时机。

<script>
function makeRequestWithUserGesture() {
  var promise = document.requestStorageAccess();
  promise.then(
    function () {
      // Storage access was granted.
    },
    function () {
      // Storage access was denied.
    }
  );
}
</script>
<button onclick="makeRequestWithUserGesture()">Play video</button>

iframe 需要遵守一套规则才能获得存储访问权限。基本规则如下:

  • iframe 的 Cookie 需要当前由 ITP 进行分区。如果不是,则 iframe 要么已经拥有 Cookie 访问权限,要么无法获得访问权限,因为其 Cookie 已被清除。
  • iframe 需要是顶级框架的直接子级。
  • 在 API 调用时,iframe 需要正在处理用户手势。

以下是调用 document.requestStorageAccess() 返回的 Promise 的详细规则。当我们说 eTLD+1 时,我们指的是有效顶级域 + 1。eTLD 是 .com 或 .co.uk,因此 eTLD+1 的一个例子是 social.co.uk,而不是 sub.social.co.uk (eTLD+2) 或 co.uk (仅 eTLD)。

  1. 如果子框架处于沙盒模式但没有“allow-storage-access-by-user-activation”和“allow-same-origin”令牌,则拒绝。
  2. 如果子框架的父级不是顶级框架,则拒绝。
  3. 如果浏览器未处理用户手势,则拒绝。
  4. 如果子框架的 eTLD+1 等于顶级框架的 eTLD+1,则解析。例如,login.socialexample.co.uk 与 www.socialexample.co.uk 具有相同的 eTLD+1。
  5. 如果子框架源的 Cookie 当前被阻止,则拒绝。这意味着 ITP 要么已清除该源的网站数据,要么将在不久的将来这样做。因此,没有存储可供访问。
  6. 如果以上所有条件均已通过,则解析。

访问权限移除

只要文档的框架附加到 DOM,存储访问权限就在文档的生命周期内被授予。这意味着

  • 当子框架导航时,访问权限被移除。
  • 当子框架从 DOM 中分离时,访问权限被移除。
  • 当顶级框架导航时,访问权限被移除。
  • 当网页消失时(例如关闭选项卡),访问权限被移除。

沙盒 iframe

如果嵌入网站已将 iframe 置于沙盒中,则默认情况下不能授予其存储访问权限。嵌入网站需要添加沙盒令牌“allow-storage-access-by-user-activation”,以允许成功的存储访问请求。iframe 沙盒还需要“allow-scripts”和“allow-same-origin”令牌,否则它将无法调用 API,也无法在可以拥有 Cookie 的源中执行。

<iframe sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin"></iframe>

关于潜在滥用的注意事项

我们决定当 iframe 调用 Storage Access API 时不提示用户,以使用户体验尽可能流畅。ITP 的规则是授予访问权限的有效守门人,目前我们依赖这些规则。

然而,我们将监控该 API 的采用情况,如果发现用户在调用 iframe 中明显没有尝试进行任何身份验证操作的广泛滥用行为,我们将进行更改。此类 API 行为更改可能包括提示、导致 Promise 被拒绝的滥用检测、限制每个源的 API 调用速率等。

可用性

Storage Access API 在 iOS 11.3 beta 和 macOS High Sierra 10.13.4 beta 上的 Safari 11.1 以及 Safari 技术预览版 47+ 中可用。如果您对跨浏览器兼容性感兴趣,请关注 Storage Access API 的 whatwg/html issue

反馈

请通过 bugs.webkit.org 报告 Bug,或在 Twitter 上向团队 @webkit 或我们的推广大使 @jonathandavis 发送反馈。如果您对 Storage Access API 的工作原理有技术问题,可以在 Twitter 上找到我 @johnwilander