Storage Access API
存储访问API提供了一种方式,允许以第三方上下文(即嵌入在 中)加载的跨站点内容访问通常只能在第一方上下文(即直接加载到浏览器标签页中)才能访问的第三方Cookie和未分区状态。
存储访问API与默认阻止访问第三方Cookie和未分区状态以提高隐私(例如,防止跟踪)的用户代理相关。即使在这些默认限制存在的情况下,我们仍然希望启用第三方Cookie和未分区状态的合法用途。示例包括与联邦身份提供商 (IdP) 的单点登录 (SSO),或者在不同站点之间持久化用户详细信息(如位置数据或查看偏好)。
该API提供了方法,允许嵌入式资源检查它们当前是否具有第三方Cookie访问权限,如果否,则向用户代理请求访问权限。
概念与用法
浏览器实现了多种存储访问功能和策略,限制了对第三方Cookie和未分区状态的访问。这些功能从为每个顶级源下的嵌入式资源提供唯一的Cookie存储空间(分区Cookie)到在第三方上下文中加载资源时完全阻止Cookie访问。
第三方Cookie和未分区状态阻止功能和策略的语义因浏览器而异,但核心功能相似。嵌入在第三方上下文中的跨站点资源无法访问它们在第一方上下文中可以访问的相同状态。这样做是出于善意——浏览器供应商希望采取措施更好地保护用户的隐私和安全。例如,减少用户在不同站点上的活动被跟踪的可能性,并降低受到跨站请求伪造(CSRF)等漏洞攻击的风险。
然而,嵌入式跨站点内容访问第三方Cookie和未分区状态存在合法用途,而上述功能和策略已知会破坏这些用途。假设您有一系列提供不同产品的网站——heads-example.com、shoulders-example.com、knees-example.com和toes-example.com。
或者,您可能会出于本地化目的将内容或服务分离到不同的国家/地区域——example.com、example.ua、example.br等——或以其他方式进行分离。
您可能拥有伴随的实用工具站点,其中包含嵌入在所有其他站点中的组件,例如,用于提供SSO(sso-example.com)或通用个性化服务(services-example.com)。这些实用工具站点将希望通过Cookie与它们嵌入的站点共享其状态。它们无法共享第一方Cookie,因为它们位于不同的域中,而第三方Cookie在阻止它们的浏览器中将不再起作用。
在这种情况下,站点所有者通常会鼓励用户将他们的站点添加为例外,或完全禁用第三方Cookie阻止策略。希望继续与其内容交互的用户必须显著放宽对所有嵌入式源以及可能所有网站加载资源的阻止策略。
存储访问API旨在解决此问题;嵌入式跨站点内容可以通过 Document.requestStorageAccess() 方法逐帧请求对第三方Cookie和未分区状态的无限制访问。它还可以通过 Document.hasStorageAccess() 方法检查是否已拥有访问权限。
未分区与分区Cookie
值得注意的是,存储访问API仅需要提供对“未分区”第三方Cookie的访问。这意味着自早期Web以来以传统方式存储的Cookie——所有在同一站点上设置的Cookie都存储在同一个Cookie罐中。这与“分区”Cookie形成对比,后者为每个顶级站点下的嵌入式资源提供唯一的Cookie存储空间,从而使得通过这些Cookie跨站点跟踪用户成为不可能。
浏览器具有各种机制来分区第三方Cookie访问,例如 Firefox Total Cookie Protection 和 具有独立分区状态的Cookie(CHIPS)。
当我们在存储访问API的上下文中谈论第三方Cookie时,我们隐含地指的是“未分区”第三方Cookie。
工作原理
合法需要第三方Cookie或未分区状态访问的嵌入式内容可以使用存储访问API请求访问,具体如下:
- 它可以调用
Document.hasStorageAccess()方法来检查它是否已经拥有所需的访问权限。 - 如果否,它可以通过
Document.requestStorageAccess()方法请求访问。 - 根据浏览器的不同,用户会被以略微不同的方式询问是否授予请求嵌入的访问权限。
- Safari 会向所有之前未获得存储访问权限的嵌入式内容显示提示。
- Firefox 仅在某个源在超过阈值数量的站点上请求存储访问后才会提示用户。
- Chrome 会向所有之前未获得存储访问权限的嵌入式内容显示提示。但是,如果嵌入式内容和嵌入站点属于同一个 相关网站集,它将自动授予访问权限并跳过提示。
- 访问的授予或拒绝基于内容是否满足所有安全要求——有关一般要求,请参阅安全措施;有关某些浏览器特定的安全要求,请参阅浏览器特定差异。
requestStorageAccess()基于 Promise 的特性允许您运行代码来处理成功和失败的情况。- 现代规范行为规定访问权限是“逐帧”授予的——每个独立的内容嵌入默认都会阻止其第三方 Cookie 访问,并且需要调用
requestStorageAccess()才能选择启用访问。如果某个内容嵌入已获得访问权限,并且同站嵌入随后调用requestStorageAccess(),它们的 Promise 将自动履行。但它们仍然需要选择启用。 - “默认阻止”行为的唯一例外是当内容嵌入成功调用
requestStorageAccess()后,然后执行同源导航(例如重新加载自身)。在这种情况下,存储访问权限会从之前的导航中继承。 - 在旧的规范版本中,访问权限是“逐页”的(Safari 是目前唯一仍在使用此模型的浏览器)。当一个嵌入通过
requestStorageAccess()获得第三方 Cookie 访问权限时,所有其他同站嵌入都会自动获得访问权限。从安全角度来看,这不是理想的行为——例如,如果shop.example.com嵌入了locator.users.com以允许用户在购物时使用他们的位置信息,并且locator.users.com调用了requestStorageAccess(),那么shop.example.com及其嵌入的任何其他站点都将能够访问其 Cookie,但也会访问来自private.users.com的 Cookie,而后者并非旨在被嵌入。阅读有关此更改背后的动机的更多信息。
- 现代规范行为规定访问权限是“逐帧”授予的——每个独立的内容嵌入默认都会阻止其第三方 Cookie 访问,并且需要调用
- 一旦授予访问权限,浏览器中会存储一个权限密钥,其结构为
<顶级站点, 嵌入站点>。例如,如果嵌入站点是embedder.com,嵌入内容是locator.example.com,则密钥将是。然后,同站嵌入(docs.example.com、profile.example.com等)将能够调用requestStorageAccess(),并且Promise将自动实现,如前所述。- 旧的规范版本使用了更具体的权限密钥结构
<顶级站点, 嵌入源>,这意味着同站、跨源嵌入不匹配权限密钥,必须单独经历整个过程。
- 旧的规范版本使用了更具体的权限密钥结构
注意: 在顶级站点其 Cookie 被分区的情况下,存储访问API不是必需的,因为默认共享 Cookie 没有隐私风险。
安全措施
有几个不同的安全措施可能导致 Document.requestStorageAccess() 调用失败。如果您在尝试使请求正常工作时遇到问题,请检查以下列表:
-
该调用必须与用户手势(瞬态激活)相关联,例如轻触或点击。这可以防止页面上的嵌入内容用过多的访问请求来骚扰浏览器或用户。请注意,在以下情况下不需要这样做:
- 已授予使用 API 的权限,例如通过另一个同站资源调用
requestStorageAccess()。 - 调用方是顶级文档或与顶级文档同站。在这种情况下,可能根本不需要调用
requestStorageAccess()。
- 已授予使用 API 的权限,例如通过另一个同站资源调用
-
文档和顶级文档的源不能为
null。 -
从未作为第一方交互过的源没有第一方存储的概念。从用户的角度来看,他们与该源只有第三方关系。如果浏览器检测到用户最近没有在第一方上下文中与嵌入内容交互(在 Firefox 中,“最近”指30天内),则访问请求将自动被拒绝。
-
文档的窗口必须是一个安全上下文。
-
由于安全原因,默认情况下,沙盒化
无法获得存储访问权限。因此,API 还添加了allow-storage-access-by-user-activation沙盒令牌。嵌入网站需要添加此令牌,以及allow-scripts和allow-same-origin,以允许执行脚本来调用 API 并在可以拥有 cookie/状态的源中执行它,才能使存储访问请求成功。html<iframe sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin"> … </iframe> -
此功能的使用可能会被服务器上设置的
storage-access权限策略阻止。
注意: 文档可能还需要通过额外的浏览器特定检查。例如:允许列表、阻止列表、设备端分类、用户设置、反点击劫持启发式方法,或提示用户明确许可。
浏览器特定差异
尽管 API 接口相同,但由于存储访问策略的差异,使用存储访问 API 的网站应预期在不同浏览器之间获得的第三方 Cookie 访问级别和范围会有所不同。
Chrome
- Cookie 必须明确设置
SameSite=None,因为 Chrome 的默认值为SameSite=Lax(Firefox 和 Safari 的默认值为SameSite=None)。 - Cookie 必须设置
Secure属性。 - 存储访问权限会在浏览器使用30天且没有用户交互后逐步失效。与嵌入内容的交互会将此限制再延长30天。当调用
Document.requestStorageAccessFor()时不会发生这种情况,因为用户已经在页面上。
Firefox
- 如果嵌入源
tracker.example已经获得了顶级源foo.example上的第三方 Cookie 访问权限,并且用户在不到30天内再次访问了foo.example嵌入tracker.example页面的页面,那么嵌入源在加载时将立即获得第三方 Cookie 访问权限。 - 存储访问授权将在30个日历日后失效。
Firefox 新存储访问策略(用于阻止跟踪 Cookie)的文档包含对存储访问授权范围的详细描述。
Safari
- 存储访问权限会在浏览器使用30天且没有用户交互后逐步失效。成功使用存储访问API会重置此计数器。
示例
- 有关包含代码示例的实现指南,请参阅使用存储访问API。
API方法
Document.hasStorageAccess()-
返回一个 Promise,该 Promise 解析为一个布尔值,指示文档是否具有对第三方 Cookie 的访问权限。
-
Document.hasStorageAccess()的新名称。 Document.requestStorageAccess()-
允许在第三方上下文中(即嵌入在
中)加载的内容请求访问第三方 Cookie 和未分区状态;返回一个 Promise,如果授予访问权限则解析,如果拒绝访问权限则拒绝。 Document.requestStorageAccessFor()实验性-
存储访问 API 的一个提议扩展,允许顶级站点代表来自同一相关网站集中的另一个站点的嵌入内容请求第三方 Cookie 访问权限。返回一个 Promise,如果授予访问权限则解析,如果拒绝访问权限则拒绝。
注意: 用户交互会传播到这些方法返回的 Promise,允许调用者执行需要用户交互的操作,而无需第二次点击。例如,调用者可以从已解析的 Promise 中打开一个弹出窗口,而不会触发 Firefox 的弹出窗口阻止程序。
对其他API的补充
Permissions.query(),"storage-access"功能名称-
在支持的浏览器中,这可以查询是否通常已授予第三方 Cookie 访问权限,即授予另一个同站嵌入。如果是这样,您可以在没有用户交互的情况下调用
requestStorageAccess(),并且 Promise 将自动解决。 Permissions.query(),"top-level-storage-access"功能名称 实验性-
一个单独的功能名称,用于查询是否已通过
requestStorageAccessFor()授予访问第三方 Cookie 的权限。如果是这样,您无需再次调用requestStorageAccessFor()。
规范
| 规范 |
|---|
| Storage Access API |
| 将存储访问API(SAA)扩展到非Cookie存储 |
浏览器兼容性
api.Document.hasStorageAccess
加载中…
api.Document.hasUnpartitionedCookieAccess
加载中…
api.Document.requestStorageAccess
加载中…
api.Document.requestStorageAccessFor
加载中…
api.Permissions.permission_storage-access
加载中…
另见
- 使用 Storage Access API
- Storage Access API 介绍 (WebKit 博客)