1. 简介
本节为非规范性内容。
用户代理有时会阻止某些
iframe
内的内容访问客户端存储机制(如 cookie)中的数据。这可能会导致依赖客户端存储访问的嵌入内容出现问题。
存储访问 API 使
iframe
内的内容可以请求并获得其客户端存储的访问权限,以便依赖客户端存储访问的嵌入内容能够在此类用户代理中正常工作。[STORAGE-ACCESS-INTRO]
2. 基础设施
本规范依赖 Infra 标准。[INFRA]
3. 存储访问 API
本规范定义了一种方法,用于查询一个Document
当前是否有权访问其未分区数据(hasStorageAccess()),
以及一个可用于请求访问其未分区数据的方法(requestStorageAccess())。
Alex 访问 https://social.example/。页面设置了一个 cookie。该 cookie 在
一方网站上下文中被设置。
之后,Alex 访问 https://video.example/,该页面嵌入了一个加载
https://social.example/heart-button
的
iframe。
在这种情况下,
social.example 的 Document
doc 处于第三方上下文,而之前设置的 cookie 是否可在 doc.cookie
中访问,取决于用户代理的存储访问策略。
iframe
内的脚本可以调用 doc.hasStorageAccess()
以判断是否拥有 cookie 访问权限。如果没有访问权限,则可以通过调用 doc.requestStorageAccess()
请求访问。
未分区数据 是指若一个网站在一方网站上下文中加载时可访问的客户端存储数据。
Document
处于一方网站上下文,如果它是顶级浏览上下文的活动文档。否则,如果它是活动文档,且其origin与顶级 origin相同,它也处于一方网站上下文(需满足相关设置对象的 origin 与顶级 origin 同站)。
Document
若不处于一方网站上下文,则处于第三方上下文。
要判断用户代理是否明确允许未分区
cookie 访问,给定一个由两个网站组成的元组tuple,请执行以下步骤。该算法返回 "none"、"allow" 或
"disallow"。
注意:用户代理的设置可能通过网站白名单、用户更改全局浏览器配置或类似的自定义覆盖方式,明确允许或拒绝未分区 cookie 访问。
-
如用户代理未对 tuple 拥有未分区 cookie 访问的明确设置,则返回"
none"。 -
如用户代理的设置对 tuple 明确允许未分区 cookie 访问,则返回"
allow"。 -
断言:用户代理的设置对 tuple 明确拒绝未分区 cookie 访问。
-
返回"
disallow"。
要判断 FedCM 站点连接状态,给定 origin embedder 和 origin identityProvider,执行以下步骤。此算法返回布尔值。
-
返回 false。
要判断有效 FedCM 连接状态,给定origin embedder,origin identityProvider,Document
doc,执行以下步骤。该算法返回布尔值。
-
令 policyStatus 表示 doc 是否被允许使用 "
identity-credentials-get"。 -
如 policyStatus 为 "Disabled",返回 false。
-
令 connected 为根据 判断站点连接状态算法,传入 embedder 和 identityProvider 的结果。
-
如 connected 为 false,返回 false。
-
令 preventSilentAccess 表示 用户代理的凭据存储中 origin-防静默访问标志 对于 embedder 的值。
-
如 preventSilentAccess 为真,返回 false。
-
返回 true。
3.1. 与存储访问相关的用户代理状态更改
按如下方式修改 environment 的定义:
-
新增名为 has storage access 的成员,类型为 布尔值。
按如下方式修改 source snapshot params 的定义:
存储访问资格 可以为 "unset"、"ineligible" 或 "eligible"。
请求拥有一个存储访问资格
eligible for storage-access。其初始值为
"unset"。
注意:一个请求的存储访问资格
指示在评估此请求应包含哪些 cookie 时,
是否应考虑此前授予的 "storage-access" 权限。
特别要注意,requestStorageAccess
完成以及environment 的 has storage
access
被设为 true 后,并不是该 environment中发起的所有请求都应携带未分区 cookie。
例如,假设用户正在访问 https://top.com,该页面嵌入了来自 https://embed.com 的
iframe,
且该 iframe 中脚本已调用 requestStorageAccess
并已 resolve。如果该 iframe 随后去 fetch https://3p.com 的资源,该请求不会通过存储访问 API 附带 cookie。
“ancestry” 的概念正通过 https://github.com/whatwg/html/pull/11133 被添加到 HTML 规范
-
如果 request 的client 的has storage access 为 false,则返回 "
ineligible"。 -
如果 request 的origin与 request的url 的 origin 不同源,则返回 "
ineligible"。 -
令 allowed 为执行 Should request be allowed to use feature?,参数为 "
storage-access" 和 request,得到的结果。 -
如果 allowed 为 false,则返回 "
ineligible"。 -
返回 "
eligible"。
3.2. Document
的变更
partial interface Document {Promise <boolean >hasStorageAccess ();Promise <undefined >requestStorageAccess (); };
在 Document
doc 上调用 hasStorageAccess() 方法时,必须执行以下步骤:
-
令 p 为 一个新的 promise。
-
如果 doc 不是 完全激活,则用 "
InvalidStateError"DOMException拒绝 p 并返回 p。 -
令 global 为 doc 的 相关全局对象。
-
如果 global 不是 安全上下文,则用 false 解析 p 并返回 p。
-
令 browsingContext 为 doc 的 浏览上下文。
-
按如下步骤 并行运行:
-
令 explicitSetting 为使用 (topLevelSite, embeddedSite) 调用 判断用户代理是否明确允许未分区 cookie 访问 的结果。
-
令 permissionState 为使用 "
storage-access" 及 global 执行获取当前权限状态的结果。 -
-
若 explicitSetting 为 "
disallow",用 false 解析 p。 -
若 explicitSetting 为 "
allow",用 true 解析 p。 -
断言:explicitSetting 为 "
none"。 -
若 browsingContext 是 顶级浏览上下文,用 true 解析 p。
-
若 browsingContext 与其顶级浏览上下文的活动文档属于相同权限主体,则用 true 解析 p。
“same authority” 为未来定义概念的占位符(允许用户代理在执行 同站 检查时增加如父文档为跨站点等额外安全考量),详见 whatwg/storage#142。 实际上,这可能涉及对 cookie 站点 的比较,或与顶级文档进行一次 same site 检查。
-
若 permissionState 为 granted,用 global 的has storage access 解析 p。
注意:此处全局的存储访问权限状态优先于本地 has storage access 标志,以便于立即响应用户在设置中撤回权限的选择。
-
用 false 解析 p。
-
-
-
返回 p。
在 Document
doc 上调用 requestStorageAccess()
方法时,必须执行以下步骤:
-
令 p 为 一个新的 promise。
-
如果 doc 不是 完全激活,则用 "
InvalidStateError"DOMException拒绝 p 并返回 p。 -
令 global 为 doc 的 相关全局对象。
-
令 settings 为 doc 的 相关设置对象。
-
如果 global 不是 安全上下文,则用 "
NotAllowedError"DOMException拒绝 p 并返回 p。 -
如果 doc 不被允许使用 "
storage-access",则用 "NotAllowedError"DOMException拒绝 p 并返回 p。 -
如果 doc 的 origin 是 不透明来源,则用 "
NotAllowedError"DOMException拒绝 p 并返回 p。 -
如果 settings 的 顶级来源 是 不透明来源,则用 "
NotAllowedError"DOMException拒绝 p 并返回 p。 -
如果 doc 的 活动沙箱标志集包含 通过用户激活获取存储访问的沙箱标志,则用 "
NotAllowedError"DOMException拒绝 p 并返回 p。 -
令 browsingContext 为 doc 的 浏览上下文。
-
令 topLevelOrigin 为 doc 的 相关设置对象的顶级来源。
-
令 topLevelSite 为从 topLevelOrigin 获取的站点。
-
令 embeddedOrigin 为 doc 的 origin。
-
令 embeddedSite 为从 embeddedOrigin 获取的站点。
-
按如下步骤 并行运行:
-
令 process permission state 为一算法,给定 权限状态 state 时,执行以下步骤:
-
在 网络任务源 上为 global 排队全局任务:
-
若 state 为 granted:
-
将 global 的 has storage access 设为 true。
-
用
undefined解析 p。
-
-
否则:
-
对 global 消费用户激活。
-
用 "
NotAllowedError"DOMException拒绝 p。
-
-
-
-
令 explicitSetting 为使用 (topLevelSite, embeddedSite) 调用 判断用户代理是否明确允许未分区 cookie 访问 的结果。
-
若 explicitSetting 为 "
disallow":-
以 denied 调用 process permission state。
-
中止后续步骤。
-
-
若 explicitSetting 为 "
allow":-
以 granted 调用 process permission state。
-
中止后续步骤。
-
-
断言:explicitSetting 为 "
none"。 -
如果 browsingContext 是 顶级浏览上下文:
-
以 granted 调用 process permission state。
-
中止后续步骤。
-
-
如果 embeddedSite 与 topLevelSite 同站:
注: 这里有意用 same site 检查,以便嵌入站点在出于安全但非隐私原因受限存储访问的情况下,可自行调用
requestStorageAccess()获得访问,无需最终用户参与。-
以 granted 调用 process permission state。
-
中止后续步骤。
-
-
令 previous permission state 为使用 "
storage-access" 及 global 执行 获取当前权限状态的结果。 -
如果 previous permission state 不是 prompt:
-
以 previous permission state 调用 process permission state。
-
中止后续步骤。
-
-
令 connected 为以 topLevelOrigin、embeddedOrigin、 doc 调用 判断有效 FedCM 连接状态 的结果。
-
若 connected:
注: 鼓励用户代理追踪因已存在 FedCM 连接而允许存储访问的(站点,站点)元组, 以便访问 cookie 时再次核查该列表,从而防止攻击者诱导 environment 使用错误的 has storage access 位。
-
以 granted 调用 process permission state。
-
中止后续步骤。
-
-
若 has transient activation 为 false:
-
以 denied 调用 process permission state。
-
中止后续步骤。
-
-
令 permissionState 为对 "
storage-access" 请求权限的结果。注: 请求权限时是否弹框由用户代理实现决定,尤其对
storage-access,用户代理常有自定义规则可在不弹框时直接允许或拒绝。 -
以 permissionState 调用 process permission state。
-
-
返回 p。
注:本算法设计意图是始终要求用户激活后才设置 storage-access 权限。虽然用户代理可基于自定义启发规则在未激活时设权限,本规范强烈反对此做法,以免导致互操作问题。
3.3. 导航相关的更改
当快照化源快照参数时:
-
将has storage access设置为 sourceDocument的has storage access。
-
将environment id设置为 sourceDocument的relevant settings object的id。
对于通过 fetch 创建导航参数算法,在第3步插入以下内容:
-
令originalURL为entry的URL。
当在通过 fetch 创建导航参数中创建request的reserved client时:
-
如果全部满足以下条件,则将reserved client的has storage access设置为sourceSnapshotParams的has storage access:
-
sourceSnapshotParams的environment id等于 navigable的活动文档的relevant settings object的id。
-
response为null或response的has-cross-origin-redirects为false。
-
-
否则,将request的reserved client的has storage access设置为false。
当设置窗口环境设置对象时:
-
将settings object的has storage access设置为reserved environment的has storage access。
3.4. 与 Fetch 的集成
3.4.1. 获取流程
在fetch的第14步后插入新步骤:
-
将request的eligible for storage-access设置为 判断初始存储访问资格算法对request的结果。
3.4.2. HTTP 重定向获取
在HTTP-redirect fetch的第17步后插入新步骤:
-
如果request的eligible for storage-access不是 "
unset", 且locationURL的origin与request的current URL的origin不同源,则将request的eligible for storage-access设置为 "ineligible"。
3.5. 各类客户端存储机制的更改
该 API 仅影响 HTTP Cookie。API 的未来版本可能会影响其他客户端状态。[RFC6265]
3.5.1. Cookie
本 API 旨在配合阻止第三方上下文未分区 Cookie 访问的环境和用户代理配置使用。于当前文档发布时,该机制尚未集成到HTTP-network-or-cache fetch与cookie算法中。
为促进相关集成,cookie 存储将需支持关于请求顶级/嵌入站点及该请求是否拥有存储访问的信息(以决定是否附加跨站/分区/不附加 Cookie),并通过访问在本规范中定义的eligible for storage-access标志获悉该信息。
一旦 Cookie 存储允许获取存储访问信息,我们将更新HTTP-network-or-cache fetch与cookie,
在获取 Cookie 时,将请求的eligible for storage-access传递给cookie 存储。
从cookie 存储通过存储访问获取未分区 Cookie 时,用户代理仍会遵循 SameSite 限制(即在第三方上下文中不会附加
SameSite=Strict 或 SameSite=Lax 的 Cookie)。
注意:用户代理可能对SameSite Cookie 属性有不同默认值,这会导致未设置
SameSite 属性的未分区 Cookie
在某些用户代理(默认 SameSite=None)请求时会被附加,而在其他用户代理(默认 SameSite=Lax)不会。建议 Web 开发者为自己的 Cookie
显式设置 SameSite 属性以避免问题。
3.6. 存储访问的沙箱机制
沙箱标志集合具备一个允许用户激活后存储访问的沙箱标志。该标志会阻止内容请求存储访问。
在解析沙箱指令算法第3步下方,添加如下内容:
- 添加允许用户激活后存储访问的沙箱标志,除非tokens包含
allow-storage-access-by-user-activation关键字。
4. 权限集成
存储访问 API 定义了一个由名字
"storage-access" 标识的强特性。它定义了以下与权限相关的算法:
- 权限查询算法
-
查询 "
storage-access" 权限时,给定PermissionDescriptorpermissionDesc 与PermissionStatusstatus: - 权限键类型
-
"
storage-access" 特性的权限键为:由一个站点 top-level和一个站点 请求者组成的元组。(("https", "news.example"), ("https", "social.example"))是 "storage-access" 的权限键,其top-level为("https", "news.example"),其请求者为("https", "social.example")。 - 权限键生成算法
-
为 "
storage-access" 特性生成权限键时,给定origin origin 和 origin embeddedOrigin: - 权限键比较算法
-
比较 "
storage-access" 特性的两个权限键 key1 和 key2 时,执行:
5. 权限策略集成
存储访问 API 定义了由字符串"storage-access"标识的策略可控特性。其默认允许清单为"*"。
注意:Document的权限策略决定了该文档内的内容是否能通过requestStorageAccess()请求存储访问。
如果在任一文档中被禁用,则该文档里调用requestStorageAccess()将会被拒绝。
6. 隐私相关考量
存储访问 API 有助于移除跨站 Cookie。特别是它允许身份验证嵌入场景继续工作。因此,该 API 为开发者重新获得跨站 Cookie 访问(但会受附加约束)提供了一条路径。
嵌套Document
调用requestStorageAccess()并获得
resolved
Promise
后,
可获得与其作为顶级浏览上下文的活动文档一致的 Cookie。借助这些 Cookie,可对服务器进行身份认证并加载用户相关信息。
虽然该功能伴随第三方滥用于跟踪的风险,但 API 的明确目标之一和关键设计原则,就是不削弱跨站 Cookie
废弃带来的隐私提升。重要的是,与废弃前相比,隐私性不会变差。这归因于规范中未使用平台特有信息防止无状态跟踪,唯一新增的状态是仅限于嵌入站点与被嵌入
Document之间权限声明。
当默认已弃用跨站 Cookie 时,隐私考虑更具挑战。问题是何时及如何允许使用存储访问 API 让无 Cookie(或分区 Cookie)的嵌套Document
回到废弃前状态,获得其未分区数据访问权。
理想情况下,嵌套Document
仅在满足以下条件时才能获得其未分区数据访问权:
-
用户与嵌套
Document发生交互 -
该嵌套
Document被嵌入者允许使用 API -
用户明确、分组授权被嵌入方使用其 Cookie
-
不会因频繁请求
storage-access权限导致用户疲劳,进而削弱用户主动授权的效力
规定要求实现方保证前三点。这有助于保证用户知晓嵌套Document内容、嵌入者未拒绝其认证、跨站点
Cookie 不会暴露给网络攻击者。
最后两点存在矛盾。理想中,用户会在每次requestStorageAccess()调用时都被提示。
但,这将允许页面频繁弹窗,有违合理体验。因此用户代理需防止过度弹窗。
document.requestStorageAccess()时显示给用户。
因此,最后两点反映了关键妥协。只要满足其他要求,允许在授予或拒绝未分区数据访问时采用实现自定义行为,无需用户介入。此妥协会较弱化第4点隐私保障,但决定权归实现方,甚至可使 API 总无法获得存储访问。然而,这对满足第5点——在实现方就两点妥协持有差异立场时——是必要的。
开发者体验会因用户代理在实现自定义行为上差异较大而受损。因此用户代理应当尽量减少或统一静默授权和拒绝。
6.1. 权限范围
本 API 设计中的另一个权衡点是,"storage-access" 权限应以什么为主键:origin 还是 site。我们选择了site,因为我们认为它们在隐私上是可接受的边界,同时也支持 same site 与 cross-origin 嵌套 Documents
在同一页面只需一次用户授权弹窗的用例。
7. 安全性考量
即便与跨站 Cookie 移除后的平台相比,本规范也不应削弱 Web 平台的安全属性。第三方 Cookie 的移除有可能增进安全性,特别是在缓解依赖认证请求的攻击(如 CSRF)方面。我们不希望 Storage Access API 成为此类攻击的突破口。
因此,我们将“storage-access”权限的影响局限于:只对调用了 未分区数据 访问的嵌入 Document
生效,且权限只在该嵌入
requestStorageAccess()
并未跨 origin 边界导航前持续。这确保了只有页面主动调用 requestStorageAccess()
的 origin 才能发起认证请求,并且被嵌入页面可通过 Content Security Policy 的
"frame-ancestors"
指令控制允许的嵌入方,从而为安全保留 origin 范围上的控制能力。
7.1. 声誉攻击
这同样能有效防止另一种攻击:针对被嵌入方声誉的攻击。我们认为任何跨站认证请求都可能对被嵌入方带来声誉风险,尤其是在用户更注重隐私的今天。因此,第一方或同级跨站点导致嵌入资源带上用户认证 Cookie 的请求,就构成对该跨站点所有者声誉的攻击。这也是为何本 API 必须运行在安全上下文中,以防网络攻击者诱使被嵌入方调用本 API。
嵌入页面可以通过权限策略和嵌套 Document
沙箱化来控制哪些嵌套文档可被认证,甚至哪些可以显示权限请求。
7.2. 通知滥用
在定义 Storage Access API 时同样考虑了通知滥用。具体来说,我们要求用户必须与嵌套 Document
交互,在权限被拒绝时消耗这次激活,从而限制展示权限弹窗的条件。这可以缓解像“用户刚拒绝即反复请求权限”这类攻击。
8. 自动化
为支持用户代理自动化和应用测试,本文定义了如下 扩展命令,以便于[WebDriver] 规范集成。
8.1. 设置存储访问
| HTTP 方法 | URI 模板 |
|---|---|
| POST | /session/{session id}/storageaccess |
Set Storage Access 扩展命令可修改当前浏览上下文的存储访问策略。
远端步骤如下:
-
令 blocked 为通过 获取属性从 parameters 读取
blocked的值。 -
如果 blocked 不是 布尔值,则返回带有WebDriver 错误和 WebDriver 错误码 invalid argument。
-
令 embedded origin 为通过 获取属性从 parameters 读取
origin的值。 -
如 embedded origin 不是单个 U+002A 星号(*),则:
-
令 parsedURL 为对 embedded origin 执行 URL 解析器的结果。
-
若 parsedURL 失败,则返回带有WebDriver 错误和 WebDriver 错误码 invalid argument。
-
将 embedded origin 设为 parsedURL 的 origin。
-
-
如当前浏览上下文不是顶级浏览上下文,则返回带有WebDriver 错误和WebDriver 错误码 unsupported operation。
-
令 settings 为 doc 的 相关设置对象。
-
如果 embedded origin 是单个星号(*),则:
-
否则:
-
如上述实现自定义步骤集失败,则返回带有WebDriver 错误和错误码 unknown error。
-
返回success,数据为
null。
致谢
本规范基于前任编辑 John Wilander(存储访问 API 的发明者)和 Theresa O’Connor(撰写了大量初稿内容)创造的基础之上。我们感谢他们的贡献与创意。
特别感谢 Anne van Kesteren、 Ben Kelly、 Brad Girardeau、 Brad Hill、 Brady Eidson、 Brandon Maslen、 Chris Mills、 Dave Longley、 Domenic Denicola、 Ehsan Akhgari、 Geoffrey Garen、 Jack Frankland、 James Coleman、 James Hartig、 Jeffrey Yasskin、 Kushal Dave、 Luís Rudge、 Maciej Stachowiak、 Matias Woloski、 Mike O’Neill、 Mike West、 Pete Snyder、 Rob Stone、 Stefan Leyhane、 Steven Englehardt、 Travis Leithead、 Yan Zhu、 Zach Edwards、 以及所有在 whatwg/html#3338、privacycg/proposals#2 和 privacycg/storage-access/issues 回复过本提案的朋友们。
感谢 WebKit 开源项目允许我们使用 存储访问 API 提示 图片,该图片最初发布于 webkit.org。