连接允许列表

社区组报告草案,

此版本:
https://wicg.github.io/connection-allowlists/
问题跟踪:
GitHub
规范内嵌
编辑:
Noam Rosenthal (Google)
Mike West (Google)

摘要

Connection-Allowlist 机制提供了一种简明的策略语言和交付机制, 用于对上下文与其他服务器通信的能力施加一组约束。其目标是 让开发者能够以精准适配问题的方式,整体缓解显式外泄通道。

本文档状态

本规范由 Web 平台孵化器 社区组发布。 它不是 W3C 标准,也不在 W3C 标准轨道上。 请注意,根据 W3C 社区贡献者许可协议 (CLA), 适用有限的退出权以及其他条件。 了解更多关于 W3C 社区组和商务组的信息。

1. 引言

开发者希望控制加载到其页面上下文中的资源,以及其页面可以向其发出请求的 端点。这种控制出于若干目的而必要, 包括限制用户数据可通过用户代理流动的方式(缓解 外泄攻击),以及确保对站点架构和依赖项的控制。

内容安全策略满足了其中一部分需求,但其方式比最关键用例所需的更细粒度, 并且语法和文法也因 CSP 用于部署的其他保护措施而变得复杂。 [CSP]

`Connection-Allowlist` 从 CSP 中退后一步,专注于单一用例:控制 页面可通过 Fetch 和其他 Web 平台 API(WebRTC、Web Transport、FedCM、Web Payments、DNS Prefetch 等)发起的显式请求, 其方式旨在直观而全面。

NOTE: '\' line wrapping per RFC 8792

Connection-Allowlist: (response-origin "https://cdn.example" "https://*.example.:tld" \
                       "https://api.example:*"); report-to=ReportingAPIEndpoint

此标头随用户导航到的文档一起交付时,会将该 文档限制为仅允许向匹配列表中指定 URL 模式 [URLPATTERN] 的端点发出请求和连接:交付该文档的源、 https://cdn.example、倒数第二个 DNS 标签为 example 的任何主机的任何子域、 任意端口上的 https://api.example,等等。

尝试连接到不匹配允许列表的端点将被阻止,并通过 在 report-to 参数中指定的 Reporting API [REPORTING] 端点进行报告(并 通过单独的 Reporting-Endpoints 标头定义)。

1.1. 威胁模型

本提案有意保持小而聚焦,面向客户端攻击 和/或配置错误中一个具体但有用的细分领域:

1.2. 与内容安全策略的重叠

本提案与内容安全策略对给定上下文内资源使用施加限制的方式有很多共同点, 尤其是 fetch 指令。不过,出于以下几个原因,探索它似乎 是合理的:

  1. CSP 的模型过于细粒度:希望缓解数据从敏感上下文流出的风险的开发者 需要一种保护措施,能够穷尽覆盖所有可能发出 请求或建立连接的方式。CSP 将请求分类为可被 单独控制的类型,这不是处理该问题的正确方式,因为数据通过 Web 字体请求泄漏与通过图片 或脚本请求泄漏一样糟糕。区分这些请求类型会以根本无关的问题 使设计合理防御的过程复杂化。

  2. CSP 的语法又不够细粒度:CSP 支持的 host-source 文法导致 响应中交付的标头非常冗长。一个独立策略提供了 转向 URLPattern 语法的机会;这种语法可以通过提供更现代、 更灵活且标准化的匹配语法,解决人们对 CSP 方式提出的一些抱怨。

  3. CSP 的覆盖范围不完整:虽然 CSP 很好地覆盖了通过 Fetch 运行的 HTTP 请求,但它并没有穷尽覆盖 Web 平台 API 允许建立连接的无数方式。DNS 预取和 WebRTC 是很好的起点, 但还有许多其他机制一直难以明确它们究竟如何适配 CSP 的 威胁模型。通过创建一个聚焦范围较窄并向开发者作出明确承诺的新策略, 这些讨论将拥有可辩护的答案,并向规范作者提供清晰的授权。

2. 连接允许列表

连接 允许列表表示给定上下文 被允许连接到的一组 URL 模式。 它是一个包含以下 项的 结构体:

3. 连接允许列表标头

Connection-Allowlist 响应 标头包含 一组序列化的 URL 模式字符串,这些字符串定义上下文被允许 连接到的端点集合。该允许列表会针对给定上下文执行,阻止不匹配 所声明模式的出站连接。Connection-Allowlist-Report-Only 响应 标头是只报告变体,以相同方式解析,但只 发送违规 报告,而不阻止出站连接。

这些 Connection Allowlist 标头是 结构化标头,其值是一个由 列表组成的 内层 列表。服务器可以交付包含 任意数量项的列表,但只会使用第一个项。列表中的任何额外项 都会被忽略。

内层列表可以包含序列化为 字符串的 URL Patterns,或 token response-origin,它表示 一个匹配 响应的 URL 的 源的模式。意外的值将被忽略。

内层列表可以有任意 参数:

所有其他参数都将被忽略。

3.1. 解析

要解析响应的 Connection Allowlists,给定一个 response (response):
  1. 令 allowlists 为空 列表。

  2. 令 header 为从 response 的 标头列表中, 以 列表形式 获取结构化字段值 名为 `Connection-Allowlist` 的结果。

  3. 给定 header、response 的 URL,以及 enforce, 解析 Connection Allowlist 标头。如果 结果不是 null,则将其插入 allowlists。

  4. 令 header 为从 response 的 标头列表中, 以 列表形式 获取结构化字段值 名为 `Connection-Allowlist-Report-Only` 的结果。

  5. 给定 header、response 的 URL,以及 report, 解析 Connection Allowlist 标头。如果 结果不是 null,则将其插入 allowlists。

  6. 返回 allowlists。

要解析 Connection Allowlist 标头,给定 一个 结构化标头 列表 (list)、一个 URL (response-url),以及一个 disposition (disposition):
  1. 如果 list 的 大小为 0,返回 null。

  2. 如果 list[0] 不是 内层列表,返回 null。

  3. 令 allowlist 为一个 Connection Allowlist,其 disposition 为 disposition。

  4. 对 list[0] 中的每个 item 执行:

    1. 令 serialized pattern 为 null。

    2. 如果 item 是 token response-origin:

      1. 将 serialized pattern 设置为 response-url 的 源的 ASCII 序列化。

    3. 如果 item 是 字符串,则将 serialized pattern 设置为 item。

    4. 如果 serialized pattern 为 null,则继续。

    5. 令 URL pattern 为执行从 HTTP 结构化字段值构建 URL 模式的结果,给定 serialized pattern, 并以 null 作为基准 URL。

      如果此步骤抛出错误,则继续。

    6. 将 URL pattern 追加到 allowlist 的 allowlist。

  5. 对 list[0] 的 参数中的每个 key → value 执行:

    1. 如果 key 是 report-to 且 value 是一个 token,则将 allowlist 的 reporting endpoint 设置为 value。

    2. 如果 key 是 redirects 且 value 是一个 token:

      1. 如果 value 是 "block",则将 allowlist 的 redirects 设置为 block。

      2. 否则,将 allowlist 的 redirects 设置为 allow。

    3. 如果 key 是 webrtc 且 value 是一个 token:

      1. 如果 value 是 "block",则将 allowlist 的 webrtc 设置为 block。

      2. 否则,将 allowlist 的 webrtc 设置为 allow。

  6. 返回 allowlist。

注: 我们会在 解析算法中跳过任何无效输入。我们完全可以在解析上更加严厉, 但那可能会限制我们未来的灵活性。

3.2. 匹配

根据正在建立的连接类型,我们可能有一个 request 可用, 也可能只有一个 URL,甚至可能更少。例如 dns-prefetch 只能针对主机进行匹配。下面的算法说明了连接允许列表 检查在这些场景中的工作方式:

要将 URL 匹配到 Connection Allowlist, 给定一个 URL (url) 和一个 connection allowlist (connection allowlist),执行 以下步骤, 它们返回 success 或 failure。
  1. 如果 url 是本地的,返回 success。

  2. 对 connection allowlist 的 allowlist 中的每个 pattern 执行:

    1. 如果给定 pattern 和 url 的 URL 模式匹配不返回 null,则返回 success。

  3. 返回 failure。

要将主机匹配到 Connection Allowlist, 给定一个 host (host) 和一个 connection allowlist (connection allowlist),执行 以下 步骤,它们返回 success 或 failure。
  1. 对 connection allowlist 的 allowlist 中的每个 pattern 执行:

    1. 令 input 为一个新的 URLPatternInit 字典,其 hostname 被 设置为 pattern 的 hostname component。

    2. 令 host-only pattern 为给定 input、以 null 作为基准 URL,以及以一个空 map 作为选项来创建 URL 模式的结果。

    3. 令 synthetic url 为将 "https://" 与 host 拼接后 作为 URL 解析的结果。

    4. 如果给定 host-only pattern 和 synthetic url 的 URL 模式匹配不 返回 null,则返回 success。

  2. 返回 failure。

注: 通过创建一个只包含 hostname component 的新模式,并为 host 合成一个 URL, 如果允许列表中的 _任何_ 模式可能允许使用任意协议、任意端口、任意路径等 向该主机发出请求,我们就能返回匹配。

should url be blocked by Connection Allowlists 算法接受一个 URL (url)、一个 environment (environment),以及一个由 connection allowlists 组成的 列表 (connection allowlists)。它返回 allowed 或 blocked:
  1. 对 connection allowlists 中的每个 connection allowlist 执行:

    1. 如果 url matches connection allowlist,则继续。

    2. 给定 url、environment 和 connection allowlist, 报告违规。

    3. 如果 connection allowlist 的 disposition 是 enforce, 则返回 blocked。

  2. 返回 allowed。

should request be blocked by Connection Allowlists 算法 接受一个 request (request),并返回 allowed 或 blocked:
  1. 令 allowlists 为 request 的 policy container 的 connection allowlists。

  2. 对 allowlists 中的每个 allowlist 执行:

    1. 如果 request 的 URL list 的 大小 大于 1:

      1. 如果 allowlist 的 redirects 是 allow,则 继续。

      2. 给定 request 的 url、request 的 client,以及 allowlist, 报告违规。

        注: 当重定向被 阻止时,我们有意报告 request 的 url,而不是其 current url,以避免泄漏 关于重定向目标的不必要信息。

      3. 如果 allowlist 的 disposition 是 enforce, 则返回 blocked。

      4. 继续。

    2. 如果 request 的 url matches allowlist,则 继续。

    3. 给定 request 的 url、request 的 client,以及 allowlist, 报告违规。

    4. 如果 allowlist 的 disposition 是 enforce, 则返回 blocked。

  3. 返回 allowed。

should host be blocked by Connection Allowlists 算法接受 一个 host (host)、一个 environment (environment),以及一个由 connection allowlists 组成的 列表 (connection allowlists)。它返回 allowed 或 blocked:
  1. 对 connection allowlists 中的每个 connection allowlist 执行:

    1. 如果 host host-matches connection allowlist,则继续。

    2. 给定 host、environment 和 connection allowlist, 报告违规。

    3. 如果 connection allowlist 的 disposition 是 enforce, 则返回 blocked。

  2. 返回 allowed。

should WebRTC be blocked by Connection Allowlists 算法接受 一个 environment settings object (environment),并 返回 allowed 或 blocked:
  1. 令 allowlists 为 environment 的 policy container 的 connection allowlists。

  2. 对 allowlists 中的每个 allowlist 执行:

    1. 如果 allowlist 的 webrtc 是 allow,则继续。

    2. 给定 "webrtc"、environment 和 allowlist, 报告违规。

    3. 如果 allowlist 的 disposition 是 enforce, 则返回 blocked。

  3. 返回 allowed。

3.3. 报告

与其他策略机制一样,Connection Allowlists 会将每个违规报告给 允许列表标头中指定的 Reporting API 端点。违规由以下字典 类型表示:

enum ConnectionAllowlistDisposition { "enforce", "report" };

dictionary ConnectionAllowlistViolationReport : ReportBody {
  USVString url;
  USVString connection;
  sequence<DOMString> allowlist;
  ConnectionAllowlistDisposition disposition;
};

ConnectionAllowlistViolationReport 的 connection 是违反允许列表的连接的 序列化 URL。

ConnectionAllowlistViolationReport 的 allowlist 是被违反的 allowlist。

ConnectionAllowlistViolationReport 的 disposition 是该 allowlist 的 disposition。

要报告违规,给定一个 URL 或 字符串 "webrtc" (resource URL)、一个 environment (environment),以及一个 connection allowlist (allowlist):
  1. 如果 allowlist 的 reporting endpoint 是 null,则返回。

  2. 令 violation 为一个新的 ConnectionAllowlistViolationReport, 初始化如下:

url

environment 的 creation URL,并剥离以供报告使用。

connection

如果 resource URL 是一个 URL,则为 resource URL,并剥离以供报告使用。

否则,为 resource URL。

allowlist

一个新列表,包含对 allowlist 的 allowlist 中每个模式进行序列化的结果

disposition

allowlist 的 disposition。

  1. 给定 environment 作为上下文、"connection-allowlist" 作为 类型、allowlist 的 reporting endpoint 作为 目标,并以 violation 作为数据,生成并排队一个报告。

4. 嵌入式强制执行

文档通常会嵌入由其他服务器交付的内容,并希望确保嵌入的 内容至少受到其所要求的同等严格约束。 `Connection-Allowlist` 标头允许 服务器约束其自身的连接,但无法让嵌入方控制应用于其所嵌入文档的 连接允许列表。 本节定义了一种选择加入 机制,该机制以 [csp-embedded-enforcement] 为模型,使 嵌入方能够要求被框架嵌入的文档采用特定的 连接 允许列表。

嵌入方通过 connectionAllowlist 内容属性,声明其要求框架采用的 连接允许列表。用户代理通过 `Sec-Required-Connection-Allowlist` 请求标头,将该 要求传达给被框架嵌入的文档。 被框架嵌入的文档可以通过断言一个 `Connection-Allowlist` 来选择加入,该允许列表 满足该要求;也可以通过返回一个 `Allow-Connection-Allowlist-From` 响应标头来选择加入,该标头指定嵌入方的源。从本地方案 (例如 about:srcdoc 和 data:)交付的文档会隐式选择加入,因为它们会继承其 嵌入方的 策略容器。未选择加入的框架会被阻止。

当框架选择加入时,所要求的连接允许列表会被添加到被框架嵌入的Document 的 策略容器中,与文档为 自身断言的任何允许列表并存,并且该要求会通过所要求的连接允许列表由框架的后代继承。

嵌入方将某个部件限制为只能与 https://good.example 和该部件自身的 源通信:
<iframe connectionAllowlist='("https://good.example" response-origin)'
        src="https://widget.example/"></iframe>

用户代理会随导航请求一同发送该要求:

GET / HTTP/1.1
Host: widget.example
Sec-Required-Connection-Allowlist: ("https://good.example" response-origin)

该部件可以通过确认嵌入方的源来选择加入……

Allow-Connection-Allowlist-From: https://embedder.example

……也可以通过断言一个连接允许列表来选择加入,该允许列表满足 该要求:

Connection-Allowlist: ("https://good.example" response-origin)

无论采用哪种方式,框架都会加载,并且生成的Document 会 被限制为只能连接 https://good.example 和其自身的源。如果该部件既未返回任何一个标头(并且也不是 从本地方案交付的),则该框架会被阻止。

4.1. connectionAllowlist 属性

partial interface HTMLIFrameElement {
  [CEReactions] attribute DOMString connectionAllowlist;
};

connectionAllowlist 内容属性 给出嵌入方要求由 iframe 框架嵌入的Document 所采用的连接 允许列表。其 值使用与 `Connection-Allowlist` 标头相同的语法:由内部 列表组成的结构化标头 列表(参见§ 3 连接允许列表标头)。

connectionAllowlist IDL 属性必须 反映 connectionAllowlist 内容属性。

注:与服务器断言的 `Connection-Allowlist` 不同,后者由文档应用于自身,而 connectionAllowlist 属性由嵌入方设置,并应用于另一个文档。 为了 避免允许嵌入方在跨源内容不知情或不愿接受的情况下静默施加策略,被框架嵌入的 文档必须选择加入;参见§ 4.4 获取并强制执行 要求。

4.2. 嵌入式强制执行标头

Sec-Required-Connection-Allowlist 请求标头传达 嵌入方要求被框架嵌入文档采用的连接 允许列表。其值是一个使用 `Connection-Allowlist` 语法的结构化标头列表。由于 其名称带有 Sec- 前缀,因此它是禁止的请求标头,所以它只能由用户代理设置, 脚本无法设置或修改它。

Allow-Connection-Allowlist-From 响应标头允许 文档选择加入,让嵌入方对其强制执行所要求的连接允许列表。它 与 Allow-CSP-From 共享相同的语法和比较方式, 并按照 Fetch 匹配 Access-Control-Allow-Origin 的方式,精确匹配嵌入方的源:

Allow-Connection-Allowlist-From = [=origin-or-null=] / [=wildcard=]
如果以下 步骤返回 true,则称一个响应(response)允许来自导航请求(request)的连接允许列表:
  1. 令 origin 为从 response 的标头列表中获取 `Allow-Connection-Allowlist-From` 的结果。

  2. 如果 origin 是 `*`,则返回 true。

  3. 如果使用 request 对请求源进行字节序列化的结果是 origin, 则返回 true; 否则返回 false。

注:这与 Allow-CSP-From 和 CORS 检查相对应:该标头会与嵌入方的 序列化请求源(或通配符)逐字节比较; 它完全不会被 解析(既不会被解析为URL,也不会被解析为源)。

4.3. 比较允许列表

一个允许列表(a)是其子集,对于另一个 允许列表(b),如果对于 a 中的每个 pattern,b 包含一个URL 模式结构体 ,该结构体等于 pattern。

注:这是一个(非严格)子集关系:a 中的每个 URL 模式都必须出现在 b 中, 但 a 和 b 可以相等。因此,恰好声明该要求的响应 满足该要求。

注:上述比较仅要求 响应所声明的URL 模式存在于 要求中,而不会对这些模式进行任何语义分析来确定它们是否在 逻辑上被包含。这是有意为之:鉴于其语法,要判定一个URL 模式是否完全 涵盖 另一个模式是一个困难的问题,而保守的集合成员关系对于 当今开发者的用例已经足够。

注:因为被比较的允许列表都是相对于 同一个URL解析的,所以 response-origin 令牌 在每一个允许列表中的解析结果都相同。

一个连接 允许列表(candidate)满足另一个连接 允许列表 (requirement),如果以下条件全部为真:

注:空的 candidate 允许列表不允许任何端点,并且(由于它是 每个允许列表的子集)因此 满足任何其重定向和 WebRTC 处置方式也同时满足的要求。

注:“满足” 表示本节中通篇使用的“至少同样严格”关系: 允许列表比较(是其子集)处理URL 模式, 而重定向和webrtc 处置方式 则 作为单独的条件处理。

4.4. 获取并强制执行要求

我们向 策略容器结构体添加一个所要求的连接允许列表项目,该项目为 null 或字符串(采用 `Connection-Allowlist` 标头语法表示的序列化要求),其初始值为 null。它表示 某个上下文对其嵌入的文档(和 worker)施加的要求。该要求会由本地方案文档和 worker 直接继承,因为策略容器会被复制到 这些上下文中,因此受约束的上下文无法通过嵌入限制更宽松的 子上下文来逃避其约束。

注:该要求被存储为序列化的字符串, 而不是已解析的连接允许列表,这样 当该要求沿框架树向下继承时,response-origin 令牌 就可以分别相对于每个被框架嵌入的 响应的URL 进行解析。 只解析一次(在嵌入方处)会将 response-origin 固定为嵌入方的源,并 将其错误地应用于后代。这与 [csp-embedded-enforcement] 相对应,后者同样以序列化形式 将其要求存储在策略容器上,并且仅在应用 时才进行解析。

注:将要求存储在策略容器上(而不是存储在Document 上) 遵循 [csp-embedded-enforcement] 对类似策略容器项目的处理方式, 并且正是这种做法使同一 机制也能够约束 worker。

要为一个子可导航对象 (navigable)确定所要求的连接 允许列表:
  1. 令 container 为 navigable 的容器。

  2. 令 parent required 为 container 的节点文档的策略容器的 所要求的连接 允许列表。

  3. 如果 container 是一个 iframe 元素,该元素具有一个值(attribute value)不为空字符串的 connectionAllowlist 内容属性,并且给定 parent required, attribute value 是一个有效的 connectionAllowlist 属性值,则 返回 attribute value。

  4. 返回 parent required。

给定要求 parent required,如果以下所有条件均为 true,则称一个字符串(value)是一个有效的 connectionAllowlist 属性值:

注:框架可以收紧,但绝不能放宽 从其父级继承的要求: 仅当 connectionAllowlist 属性至少与父级的 要求同样严格时,才会采用该属性;否则会原样继承父级的要求。这与 [csp-embedded-enforcement] 的有效性 要求相对应,即 iframe 的 csp 属性必须被 嵌入方自身的要求所包含。

对于上述比较,当解析 a 所产生的连接 允许列表满足以相同方式解析 b 所产生的 连接 允许列表时,称一个字符串(a) 至少与另一个字符串 (b) 同样严格;其中,解析 a 时,会将其解析为一个结构化标头列表,然后在给定该列表的情况下解析 Connection Allowlist 标头;在这两种情况下, response-origin令牌会被视为 由字符串“response-origin”构建的字面URL 模式,而不是相对于 URL 进行解析。

注:此比较在导航时执行, 而不会将 response-origin 相对于 任何URL进行解析。由于 嵌入方的要求和框架自身的要求随后都会相对于同一个 每框架响应的URL进行解析, 因此在此处将该令牌视为与其自身相等是合理的,并且这样 可以避免依赖可能被跨源重定向更改的URL。该比较是保守的:如果框架的属性在语法上并不至少与其父级的 要求同样严格,则该框架只会原样继承该要求。

要在给定一个字符串(value)、一个URL (response-url)和一个 处置(disposition)的情况下,解析序列化的连接 允许列表 要求:
  1. 令 list 为将 value 解析为结构化标头列表的结果。

  2. 如果 list 为失败,则返回 null。

  3. 返回在给定 list、 response-url 和 disposition 的情况下,解析 Connection Allowlist 标头的结果。

要确定导航请求(request)的响应(response)对于一个 子可导航对象是否应被 Connection Allowlist 嵌入式强制执行阻止, 给定一个字符串或 null(requirement),返回 允许或 阻止:
  1. 如果 requirement 为 null,则返回允许。

  2. 令 required 为在给定 requirement、response 的URL 和 enforce 的情况下,解析 序列化的连接允许列表 要求的结果。

  3. 如果 required 为 null,则返回允许。

    注:格式错误的要求会被忽略, 而不会阻止框架。

  4. 如果 response允许来自 request 的连接允许列表,则返回允许。

  5. 令 header 为从 response 的标头列表中获取结构化字段值的结果,该字段 名为 `Connection-Allowlist`, 类型为“list”。

  6. 令 asserted 为在给定 header、response 的URL 和 enforce 的情况下,解析 Connection Allowlist 标头的结果。

  7. 如果 asserted 不为 null,并且 asserted满足 required,则返回 允许。

    注:格式错误的 `Connection-Allowlist` 会被解析为 null,因此不会被视为 选择加入。

  8. 返回阻止。

注:本地方案文档 (例如 about:srcdoc 和 data:)没有 响应,也不会经过此检查;它们会直接继承 嵌入方的策略容器 (包括其中存储的要求)。参见§ 5.2 与 HTML 的集成。

5. 猴子补丁

5.1. 与 Fetch 的集成

我们将在 Fetch § 4.1 主 fetch 中添加一项阻止检查, 与用于相同目的的其他检查并列,以处理请求:

在主 Fetch 中,我们将按如下方式调整步骤 7:
  1. 如果请求是否应因不良端口而被阻止、获取请求是否应作为混合内容而被阻止、请求是否应被内容安全策略阻止、 请求是否应被连接允许列表阻止、 或请求是否应被完整性策略 策略阻止返回已阻止,则将 response 设置为网络错误。

Fetch 还在较低层级定义了用于为不基于请求的 API 建立连接的算法。我们将接入解析 源和获取连接,以处理 DNS 预取、Web Transport 等:

在 解析源中,我们将更新签名,以接受一个可选的 环境设置对象 environment (默认为 null)。

我们将调用上述仅主机匹配算法,以确定是否有任何模式可能 允许连接到给定主机。如果没有,则解析失败。

  1. 如果 environment 不为 null:
    1. 令 allowlists 为 environment 的策略容器的连接允许列表。

    2. 如果对 origin 的主机、environment 和 allowlists 执行主机是否应被 连接允许列表阻止返回已阻止, 则返回失败。

在获取连接中,我们将更新签名,以接受一个 可选的环境设置对象或 Document environment(默认为 null):

我们将在当前步骤 2 之前添加一项检查:

  1. 如果 environment 不为 null:
    1. 令 allowlists 为 environment 的策略容器的连接允许列表。

    2. 如果对 url、environment 和 allowlists 执行URL 是否应被 连接允许列表阻止返回已阻止, 则返回失败。

对 Fetch 的更改将 要求我们向低层算法的调用点传递额外信息,以确定应使用的允许列表以及用于 报告的上下文。或许更好的做法是让这些调用点自行执行检查。 我认为集中处理这些逻辑会更容易成功,但采用 分步处理的方式可能更简单。

5.2. 与 HTML 的集成

为了将上述内容集成到 HTML 中,我们将在策略容器 结构中添加一个新的 连接允许列表项,其中包含一个由连接允许列表组成的列表。通过向从获取响应创建策略 容器算法添加一个步骤来填充该项:

  1. 使用 response 和 result 解析 Integrity-Policy 标头。

  2. 将 result 的连接允许列表设置为 给定 response 时 解析响应的 Connection Allowlist的结果。
  3. 返回 result。

此外,对 克隆策略容器 算法进行了修补,以复制 允许列表和要求:

  1. 将 clone 的完整性策略设置为 policyContainer 的完整性 策略的副本。

  2. 将 clone 的 连接允许列表 设置为 policyContainer 的 连接允许列表的副本。
  3. 将 clone 的 必需的连接 允许列表 设置为 policyContainer 的 必需的连接 允许列表。
  4. 返回 clone。

注:早期提示集成不需要进一步 更改,因为获取早期提示链接时已经使用了早期响应的 策略容器。

为了支持§ 4 嵌入式强制执行,我们遵循 [csp-embedded-enforcement] 为其 必需 CSP 所使用的模型,将嵌入器的要求从嵌入元素传递至 目标快照参数和导航参数,再传递到框架内 Document 的 策略容器。具体而言,按如下方式修补 HTML 和 Fetch:

  1. 一个 必需的连接允许列表 项被添加到 策略容器 结构体 (定义于 § 4.4 获取并强制实施 要求)。

  2. 一个必需的连接允许列表项,其值为 null 或一个 字符串,被添加到 目标快照参数 结构体。HTML 的 对目标快照参数进行快照 算法将其设置为以目标可导航对象为输入执行 确定必需的 连接允许列表 的结果。

  3. 一个相应的项被添加到 导航参数 结构体,而 导航、 通过获取创建导航参数,以及 从 srcdoc 资源创建导航参数 根据 目标快照参数 中的项设置它。

  4. 通过获取创建导航参数, 在创建 请求 后,将 `Sec-Required-Connection-Allowlist` 标头在请求的 标头列表 中的值设置为 目标快照参数 中的项,前提是该项不为 null。

  5. 从 获取响应创建策略容器 将该要求作为额外的 参数,并在填充 result 的 连接允许列表 后运行下述 应用必需的连接 允许列表 步骤。

注:与目标快照参数中已经携带的沙盒标志一样,该要求会在导航时从 iframe 容器创建快照,并在该可导航对象的每次 导航中生效,包括框架内 Document 自身发起的导航。因此,该约束会绑定同一可导航对象后续的导航,框架内内容无法通过自行导航到其他位置来规避它。 对 connectionAllowlist 内容属性的更改 会在可导航对象的下一次导航中生效,这与 sandbox 属性 的行为一致。(请注意,这与 referrerpolicy 不同,后者仅影响 容器自身发起的导航。)

要 应用必需的连接允许列表, 给定一个 策略容器 (result)、一个 字符串 或 null (requirement),以及一个 响应 (response):
  1. 如果 requirement 为 null,则返回。

  2. 令 required 为 解析序列化的 连接允许列表 要求 的结果,输入为 requirement、response 的 URL,以及 enforce。

  3. 如果 required 为 null,则返回。

  4. 如果不存在这样的 连接允许列表:它位于 result 的 连接允许列表 中,其 处置方式 为 enforce,且 满足 required,则 追加 required 到 result 的 连接允许列表。

  5. 将 result 的 必需的连接 允许列表 设置为 requirement。

注: 嵌入方的 连接允许列表 被添加到 result 的 连接允许列表 中,作为一个 额外的 条目,与响应 为自身声明的所有条目并列(包括 `Connection-Allowlist-Report-Only`)。 由于该列表中的每个允许列表 都会对文档的出站请求强制实施,因此嵌入方的要求与 文档自身的声明均适用。根据 [csp-embedded-enforcement],该要求 不会 在响应已经声明了一个 满足 它的允许列表时被追加, 因为该 声明已经满足要求,无需额外的条目。

注:将未解析的要求存储在 result 的策略容器上,正是将其传播给 后代的方式:每个后代都会根据自身的响应重新解析该要求(及其 response-origin 词元)。 因此,即使框架自身强制执行的 允许列表比该要求更严格,它仍有义务对其子级强制执行该策略。对于由 本地 方案(例如 about:srcdoc 和 data:)创建的 Document, 策略容器会 从嵌入器继承,因此该要求也会随之继承。

阻止决定在 Fetch 中作出:握手失败的导航响应会被替换 为网络错误。为以子可导航对象为目标的导航请求(request)取得响应后, 如果给定 request 和导航的必需连接允许列表时,确定该响应 是否应被 Connection Allowlist 嵌入式强制执行阻止的结果为已阻止,则用户代理返回 一个 网络错误。

围栏框架以及其他 其请求不受嵌入器的 策略容器管辖的上下文,无法受到此机制的约束。当 嵌入器要求此类上下文使用连接允许列表时,必须阻止该上下文,而不能允许其在不受约束的情况下加载。 请参阅§ 6.2 嵌入式强制执行。

此集成 有意与内容 安全策略:嵌入式强制执行 § 2.4 与 HTML 集成保持对应。这两种 机制的区别仅在于所携带的策略类型以及“至少同样严格”的比较; 理想情况下,共享的基础框架(目标快照参数/导航参数项、 请求标头钩子以及策略容器应用)应被提取到 HTML(或共享的 嵌入式策略强制执行规范)中,并以策略类型作为参数。这两个 规范采用单一机制进行对齐的工作将作为后续事项跟踪。

5.3. DNS 预取

在针对 dns-prefetch 链接类型的 获取并处理链接资源步骤中, 我们会将链接元素的节点文档传递给解析 源:

在 HTML 针对 dns-prefetch 链接类型的 获取并处理链接资源步骤中, 步骤 4 更新如下:
  1. 用户代理应在给定 partitionKey、 url 的源 以及 el 的相关设置对象 时,解析源 。

5.4. 预连接

在预连接算法中,我们会将链接选项的环境传递给获取连接:

在 HTML 的预连接算法中,步骤 5 更新如下:
  1. 用户代理应在给定 partitionKey、url 的源、useCredentials 以及 options 的环境 时,获取连接 。

5.5. 与 WebRTC 集成

为了约束 WebRTC 连接,[webrtc] 可以在确定候选项是否在管理上被禁止时,调用 WebRTC 是否应被 Connection Allowlist 阻止 算法。

5.6. 与 Service Worker 集成

除了应用 Service Worker 自身策略容器的默认行为之外, Service Worker 在使用 WindowClient API 调用导航时,还需要应用自身的连接允许列表。

navigate() 和 openWindow(url) 算法都必须按如下方式修补:

  1. 如果对 url、this 的相关设置对象,以及 this 的 相关设置对象的策略容器的连接允许列表 执行URL 是否应被 Connection Allowlist 阻止返回已阻止, 则返回一个以 "SecurityError" DOMException 拒绝的 Promise。

navigate() 将当前文档作为导航的 sourceDocument 传递。这看起来有些像是一个 疏漏。请参阅讨论。

6. 安全与隐私考量

6.1. 同源上下文

§ 1.1 威胁模型中描述的威胁模型有意限定在较窄的范围内, 开发者需要 仔细考虑如何将此处描述的允许列表机制分层纳入其防御体系。 最值得注意的是,该机制特定于上下文,而非适用于整个源。这为具有脚本访问权限的攻击者 留下了大量机会,使其可以通过找到限制较少的同源 上下文来绕过某个上下文的允许列表。与 HTML 的策略容器集成解决了其中一些 可能性,但其他可能性很可能仍然存在。例如,攻击者 可能能够沿框架树向上访问限制较少的父级,或者通过 window.open() 打开一个保留 opener 关系的新窗口。因此,将文档的源加入允许列表 (通过response-origin 或 显式指定) 本身并不是一个完整的解决方案。

在某些场景中,开发者可以通过 sandbox 属性或内容安全策略的 sandbox 指令,将采用允许列表的上下文与其正常源隔离开来,从而避免此风险。在这些情况下,不会有任何文档同源, 边界也将更容易维持。

开发者还可以使用§ 4 嵌入式强制执行中描述的嵌入式强制执行机制, 要求其框架嵌入的文档采用连接允许列表,而不是 依赖每个被框架嵌入的 文档自行约束。

6.2. 嵌入式强制执行

§ 4 嵌入式强制执行允许嵌入方要求被框架嵌入的文档采用连接 允许列表, 从而解决议题 #1。以下几项 特性可防止其成为新的攻击面:

其请求不受嵌入方策略容器控制的上下文(例如 围栏框架)无法被此机制约束。当 嵌入方要求此类上下文采用连接允许列表时,该上下文必须被阻止,而不能允许其在 不受约束的情况下加载。

6.3. Service Worker

Service Worker 使允许列表相关的情况变得复杂,正如 其他同源上下文一样。 由于它们具有一个与其管理的每个文档都不同的策略容器, 因此它们 很可能会响应由允许列表 与 Service Worker 的允许列表不同的文档发起的消息或请求。此提案遵循其他策略的设计,允许 存在这些能力上的差异。

请注意,文档的连接允许列表会在 Service Worker 拦截请求之前执行。 因此,如果某个请求被 文档自身的连接允许列表阻止,Service Worker 将永远不会看到来自该文档的该请求。

但是,Service Worker 仍可通过代表文档向外发起 fetch 请求 (例如,通过 postMessage(...)),帮助文档绕过其自身的连接允许列表。为防范此 数据外泄通道,开发者 可以随 Worker 脚本一起交付一个限制程度相同的允许列表。可以通过 在 Sec-Fetch-Dest 请求标头为 serviceworker 时有条件地发送 `Connection-Allowlist` 标头来高效实现这一点,而不是 将其应用于提供的每个脚本 请注意,对于 Service Worker,此允许列表必须是该 Worker 可能服务的任何文档的允许列表的超集。

6.4. 共享 Worker

与 Service Worker 一样,共享 Worker 具有独立的策略容器,并且可能通过 postMessage(...) 代表文档促成数据外泄。开发者同样可以随共享 Worker 脚本一起交付限制性 允许列表(通过检查 Sec-Fetch-Dest 请求标头是否为 sharedworker)。 从 blob: URL 创建的共享 Worker 存在一种特殊的绕过风险。虽然 data: URL 共享 Worker 会安全地继承其发起者的策略容器,但 blob: URL 共享 Worker 可以由具有不同连接允许列表的多个文档连接。为防止这种 跨文档绕过,建议使用连接允许列表的开发者 指定一个排除 blob: URL 的 worker-src 内容安全策略指令。

6.5. DNS

连接允许列表旨在降低通过 DNS 查询进行数据外泄的风险,方法是修改 Fetch 的 解析源算法,以便在发送 DNS 请求之前检查允许列表。这将 同时处理因使用会发送请求的元素和 API 而产生的隐式查询 (img 标签、fetch() 等),以及由诸如 dns-prefetch 之类的机制触发的显式查询。

尽管如此,允许列表依赖基于 URL 的匹配来确定给定端点是否 可接受。它无法防御 DNS 操纵,也不会限制 请求中涉及的 DNS 服务器(包括 DNS 解析可能 与多个服务器通信这一递归性质),也不会限制像 CNAME 这样的构造导致 对允许列表之外域名的额外查询。同样,允许列表无法保证 DNS 所指向的服务器就是你所预期的服务器。

开发者应通过依赖经过身份验证的 连接来降低 DNS 劫持和/或重新绑定的风险:仅将安全协议列入允许列表,会使控制 DNS 的攻击者更难将流量转移到任意端点,因为 TLS 握手将要求 持有具有相关名称的证书。

我们是否应将 允许列表的模式限制为表示安全协议的模式?或者对于非安全源, 完全放弃使用该标头?

6.6. postMessage(...)

此提案完全关注网络连接,这可能会让那些 原本期望通过显式通信通道进行的通信,例如 postMessage(message, options)、 MessageChannel、 BroadcastChannel 等也会 被涵盖的开发者感到意外。将模型扩展为也包含这些通信方式可能是合理的,因为它们都符合 基于源的模型,可以有意义地与允许列表进行比较。

6.7. 重定向

默认情况下,连接允许列表会阻止所有重定向。这是一种保守策略,旨在 防止通过开放重定向或其他服务器端重定向机制进行数据外泄。 如果文档上执行了允许列表,则任何导致重定向的请求都会被阻止, 除非允许列表明确选择允许重定向。

redirects 参数允许 开发者控制 此行为。

如果设置为 block(默认值),任何 重定向链长度大于 1 的 请求都会被阻止。

如果设置为 allow,则允许列表将 仅对初始请求执行。如果初始请求与允许列表匹配,则任何后续重定向 都将被允许,而不论其位置如何。此模式将数据安全责任 转移给服务器:一旦某个请求获准离开客户端,服务器就有责任 确保其不会将用户的数据重定向到不受信任的位置。

这种方法承认不同应用具有不同的安全要求。 高度敏感的应用可以选择阻止所有重定向,而其他应用则可以依赖其 受信任端点正确处理重定向。

考虑一个具有以下标头的文档:
Connection-Allowlist: ("https://api.example")

对 https://api.example/data 的请求返回一个重定向到 https://api.example/new-data 的 302 Found 时将被阻止,因为默认情况下会阻止 重定向。

如果标头改为:

Connection-Allowlist: ("https://api.example");redirects=allow

对 https://api.example/data 的同一请求将被允许,并且 随后的重定向 到 https://api.example/new-data(甚至 https://attacker.com/)也将被 允许。

最后,为了向前兼容,未知 token 将被视为 allow:

Connection-Allowlist: ("https://api.example");redirects=some-future-policy

在这种情况下,redirects 参数存在,但其值 some-future-policy 未知。 用户代理会将其视为 allow,因此重定向将被允许。 这使我们 将来有可能引入其他行为,而不会破坏现有 站点。

6.8. WebRTC

默认情况下,连接允许列表会阻止所有 WebRTC 连接。这是一种保守策略, 旨在降低通过 WebRTC 独特的网络 特性进行数据外泄的风险,因为仅通过 URL 模式可能难以对其进行约束。

webrtc 参数允许开发者 控制 此行为。

如果设置为 block(默认值),任何 建立 WebRTC 连接的 尝试都会被阻止。

如果设置为 allow,WebRTC 连接 将被 允许。

考虑一个具有以下标头的文档:
Connection-Allowlist: ("https://api.example")

任何建立 WebRTC 连接的尝试都将被阻止,因为 WebRTC 默认会被 阻止。

如果标头改为:

Connection-Allowlist: ("https://api.example"); webrtc=allow

WebRTC 连接将被允许。

7. 实现考量

7.1. WebSocket

请务必记住,WebSocket 连接由 http 和 https 模式涵盖,而不是 由指定 ws 或 wss 方案的模式涵盖。当我们建立 WebSocket 连接时,步骤 1 会分别将这些 WebSocket 特有的方案重写为 http 和 https。这 意味着 包含“ws://socket.example”之类模式的允许列表不会产生预期效果, 因为资源请求永远不会匹配该模式。

一致性

文档 约定

一致性要求通过 描述性断言 与 RFC 2119 术语的组合来表达。 规范性部分中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY” 和 “OPTIONAL” 应按 RFC 2119 中的说明解释。 但是,为了可读性, 本规范中这些词并不总是以全大写字母出现。

本规范中的所有文本均为规范性内容, 明确标记为非规范性的章节、示例和注释除外。[RFC2119]

本规范中的示例会以 “for example” 一词引入, 或通过 class="example" 与规范性文本分隔开来, 如下所示:

这是一个资料性示例的例子。

资料性注释以 “Note” 一词开头, 并通过 class="note" 与规范性文本分隔开来, 如下所示:

Note,这是一个资料性注释。

索引

由本 规范定义的术语

由 引用定义的术语

参考文献

规范性参考文献

[BLUETOOTH]
Web Bluetooth URL:https://bluetooth.spec.whatwg.org/
[CSP]
Mike West; Antonio Sartori. 内容安全策略 第 3 级. URL: https://w3c.github.io/webappsec-csp/
[CSP-EMBEDDED-ENFORCEMENT]
Mike West; Antonio Sartori. 内容安全策略: 嵌入式强制实施. URL: https://w3c.github.io/webappsec-cspee/
[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch 标准. 现行 标准. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; 等. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[REPORTING]
Douglas Creager; Ian Clelland; Mike West. 报告 API. URL: https://w3c.github.io/reporting/
[RFC2119]
S. Bradner. RFC 中用于 表示要求级别的关键词. 1997 年 3 月. 最佳当前实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC9651]
M. Nottingham; P-H. Kamp. HTTP 的结构化字段 值. 2024 年 9 月. 建议标准. URL: https://www.rfc-editor.org/info/rfc9651/
[SERVICE-WORKERS]
Monica CHINTALA; Yoshisato Yanagisawa. 服务工作线程 每夜版. URL: https://w3c.github.io/ServiceWorker/
[URL]
Anne van Kesteren. URL 标准. 现行标准. URL: https://url.spec.whatwg.org/
[URLPATTERN]
Ben Kelly; Jeremy Roman; 宍戸俊哉 (Shunya Shishido). URL 模式标准. 现行标准. URL: https://urlpattern.spec.whatwg.org/
[WEB-SMART-CARD]
Web 智能卡 API. 非官方提案 草案. URL: https://wicg.github.io/web-smart-card/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行 标准. URL: https://webidl.spec.whatwg.org/
[WEBRTC]
Cullen Jennings; 等. WebRTC:浏览器中的实时 通信. URL: https://w3c.github.io/webrtc-pc/
[WEBSOCKETS]
Adam Rice. WebSockets 标准. 现行 标准. URL: https://websockets.spec.whatwg.org/
[XHR]
Anne van Kesteren. XMLHttpRequest 标准. 现行 标准. URL: https://xhr.spec.whatwg.org/

IDL 索引

enum ConnectionAllowlistDisposition { "enforce", "report" };

dictionary ConnectionAllowlistViolationReport : ReportBody {
  USVString url;
  USVString connection;
  sequence<DOMString> allowlist;
  ConnectionAllowlistDisposition disposition;
};

partial interface HTMLIFrameElement {
  [CEReactions] attribute DOMString connectionAllowlist;
};

问题索引

对 Fetch 的更改将要求我们向低层级 算法的调用位置传递额外信息,以标识应使用的允许列表以及用于 报告的上下文。也许改为要求这些调用位置自行执行检查会更好。 我认为集中逻辑更有可能成功,但采用 分步处理的方法可能更简单。↵
围栏框架及其他其请求不受嵌入方 策略 容器控制的上下文无法被此机制约束。当 嵌入方要求此类上下文采用连接允许列表时,该上下文必须被阻止,而不能允许其在 不受约束的情况下加载。参见§ 6.2 嵌入式强制执行。↵
此集成有意与 内容安全策略:嵌入式 强制执行 § 2.4 与 HTML 的集成保持一致。这两种 机制仅在所携带的策略类型和“至少同样严格”的比较方式上有所不同; 理想情况下,共享的框架结构(目标 快照参数/导航 参数项目、 请求标头挂钩和策略容器应用)应被提取到 HTML(或共享的 嵌入式策略强制执行规范)中,并根据策略类型进行参数化。使这两项 规范统一采用单一机制已作为后续工作进行跟踪。↵
navigate() 会将当前文档作为导航的 sourceDocument 传递。这似乎有些像一个 疏漏。参见讨论。↵
我们是否应将允许列表的模式限制为表示安全协议的模式?或者对于 非安全源完全不支持该标头?↵