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. 威胁模型
本提案有意保持小而聚焦,面向客户端攻击 和/或配置错误中一个具体但有用的细分领域:
-
文档和 worker 的策略将由服务器以 HTTP 响应标头的形式声明。这 意味着能够操纵响应标头的攻击者仍然不在范围内。
-
文档(或 worker)所声明的策略只管辖由 _该_ 上下文发起的请求。 如果被框架嵌入的文档声明了不同的策略,那就按其声明处理(但需注意,该策略 将应用于通过本地方案(
data:、about:等)创建的上下文), 类似于上下文的 策略容器中的其他 组件,这些组件由 HTML 在创建新的文档/worker 上下文时处理。 -
连接和/或请求是本提案旨在防御的威胁。要作为 外泄防御有效,我们必须在连接建立之前阻止它们。
-
优秀的 Web 平台 API 的意外效果可能带来大量侧信道。 本提案不试图解决它们,而是仅关注 用户代理代表页面显式发起的那些请求或连接。这 当然包括
fetch()和XMLHttpRequest这些明确案例, 以及通常的资源 请求。它还包括通过不那么显式称为“请求”的通道建立的网络连接: 通过 Web Install API 获取的清单、通过 WebRTC 连接到 TURN/STUN 服务器、Web Transport 通道、DNS 预取、导航,等等。 这些都在范围内,而内存或 CPU 消耗、套接字 耗尽以及一般意义上的 XSLeaks 等更冷门的通道则不在范围内。 -
本提案只处理通信通道。它不旨在防止(甚至不旨在 大幅缓解)内容注入或跨站脚本等威胁。它只能 约束此类攻击在 _那些特定页面_ 上的影响,前提是这些页面部署了该策略, 并且应仅被视为页面防御中的一层;它本身并不足够。
-
尝试防御一部分服务器端威胁(例如开放重定向)很有诱惑力。 但我们很难以有效方式做到这一点,因为离开客户端的数据 本质上已经处于服务器控制之下。也就是说,允许开发者 选择客户端是否直接配合服务器通过重定向响应创建 到其他服务器连接的决定,似乎是合理的。我们将提供两个选项, 如果未来需要更细粒度控制,也留有潜在扩展空间:默认情况下, 无论目标为何,重定向响应都将被阻止。开发者可以通过在标头上设置参数 来选择允许所有重定向响应。详见 § 6.6 重定向。
-
同样,WebRTC 连接也很难通过 URL 模式来约束,因为它们通常涉及 动态端点发现和点对点连接。我们将提供一个全局开关,允许 开发者选择完全允许或阻止 WebRTC 连接。详见 § 5.6 WebRTC。
1.2. 与内容安全策略的重叠
本提案与内容安全策略对给定上下文内资源使用施加限制的方式有很多共同点, 尤其是 fetch 指令。不过,出于以下几个原因,探索它似乎 是合理的:
-
CSP 的模型过于细粒度:希望缓解数据从敏感上下文流出的风险的开发者 需要一种保护措施,能够穷尽覆盖所有可能发出 请求或建立连接的方式。CSP 将请求分类为可被 单独控制的类型,这不是处理该问题的正确方式,因为数据通过 Web 字体请求泄漏与通过图片 或脚本请求泄漏一样糟糕。区分这些请求类型会以根本无关的问题 使设计合理防御的过程复杂化。
-
CSP 的语法又不够细粒度:CSP 支持的
host-source文法导致 响应中交付的标头非常冗长。一个独立策略提供了 转向 URLPattern 语法的机会;这种语法可以通过提供更现代、 更灵活且标准化的匹配语法,解决人们对 CSP 方式提出的一些抱怨。 -
CSP 的覆盖范围不完整:虽然 CSP 很好地覆盖了通过 Fetch 运行的 HTTP 请求,但它并没有穷尽覆盖 Web 平台 API 允许建立连接的无数方式。DNS 预取和 WebRTC 是很好的起点, 但还有许多其他机制一直难以明确它们究竟如何适配 CSP 的 威胁模型。通过创建一个聚焦范围较窄并向开发者作出明确承诺的新策略, 这些讨论将拥有可辩护的答案,并向规范作者提供清晰的授权。
2. 连接允许列表
连接 允许列表表示给定上下文 被允许连接到的一组 URL 模式。 它是一个包含以下 项的 结构体:
-
reporting endpoint,它要么是
null,要么是 Reporting API 端点。除非另有说明,否则它是null。 -
disposition,它要么是 enforce,要么是 report。
-
redirects,它要么是 allow,要么是 block。除非另有 说明,否则它是 block。
-
webrtc,它要么是 allow,要么是 block。除非另有说明,否则它是 block。
3. 连接允许列表标头
Connection-Allowlist 响应 标头包含 一组序列化的 URL 模式字符串,这些字符串定义上下文被允许 连接到的端点集合。该允许列表会针对给定上下文执行,阻止不匹配 所声明模式的出站连接。Connection-Allowlist-Report-Only 响应 标头是只报告变体,以相同方式解析,但只 发送违规 报告,而不阻止出站连接。
这些 Connection Allowlist 标头是 结构化标头,其值是一个由 列表组成的 内层 列表。服务器可以交付包含 任意数量项的列表,但只会使用第一个项。列表中的任何额外项 都会被忽略。
内层列表可以包含序列化为
字符串的 URL Patterns,或 token
response-origin,它表示
一个匹配
响应的 URL 的
源的模式。意外的值将被忽略。
-
report-to参数的值 将被解析为一个 token,表示 Reporting API 端点 [REPORTING]。 -
redirects参数的值 将被解析为一个 token。如果存在该参数,它将用于设置允许列表的 redirects。如果值是 tokenblock,它将被设置为 block,否则 设置为 allow。 -
webrtc参数的值将被 解析 为一个 token。如果存在该参数,它将用于设置允许列表的 webrtc。如果值是 tokenblock,它将被设置为 block,否则 设置为 allow。
所有其他参数都将被忽略。
3.1. 解析
-
令 allowlists 为空 列表。
-
令 header 为从 response 的 标头列表中, 以 列表形式 获取结构化字段值 名为 `
Connection-Allowlist` 的结果。 -
给定 header、response 的 URL,以及 enforce, 解析 Connection Allowlist 标头。如果 结果不是
null,则将其插入 allowlists。 -
令 header 为从 response 的 标头列表中, 以 列表形式 获取结构化字段值 名为 `
Connection-Allowlist-Report-Only` 的结果。 -
给定 header、response 的 URL,以及 report, 解析 Connection Allowlist 标头。如果 结果不是
null,则将其插入 allowlists。 -
返回 allowlists。
-
如果 list 的 大小为 0,返回
null。 -
如果 list[0] 不是 内层列表,返回
null。 -
令 allowlist 为一个 Connection Allowlist,其 disposition 为 disposition。
-
对 list[0] 中的每个 item 执行:
-
令 serialized pattern 为
null。 -
如果 item 是 token
response-origin: -
如果 item 是 字符串,则将 serialized pattern 设置为 item。
-
如果 serialized pattern 为
null,则继续。 -
令 URL pattern 为执行从 HTTP 结构化字段值构建 URL 模式的结果,给定 serialized pattern, 并以
null作为基准 URL。如果此步骤抛出错误,则继续。
-
-
对 list[0] 的 参数中的每个 key → value 执行:
-
如果 key 是
report-to且 value 是一个 token,则将 allowlist 的 reporting endpoint 设置为 value。
-
-
返回 allowlist。
注: 我们会在 解析算法中跳过任何无效输入。我们完全可以在解析上更加严厉, 但那可能会限制我们未来的灵活性。
3.2. 匹配
根据正在建立的连接类型,我们可能有一个 request 可用,
也可能只有一个 URL,甚至可能更少。例如
dns-prefetch
只能针对主机进行匹配。下面的算法说明了连接允许列表
检查在这些场景中的工作方式:
-
对 connection allowlist 的 allowlist 中的每个 pattern 执行:
-
令 input 为一个新的
URLPatternInit字典,其hostname被 设置为 pattern 的 hostname component。 -
令 host-only pattern 为给定 input、以
null作为基准 URL,以及以一个空 map 作为选项来创建 URL 模式的结果。 -
令 synthetic url 为将 "https://" 与 host 拼接后 作为 URL 解析的结果。
-
如果给定 host-only pattern 和 synthetic url 的 URL 模式匹配不 返回
null,则返回 success。
-
-
返回 failure。
注: 通过创建一个只包含 hostname component 的新模式,并为 host 合成一个 URL, 如果允许列表中的 _任何_ 模式可能允许使用任意协议、任意端口、任意路径等 向该主机发出请求,我们就能返回匹配。
-
令 allowlists 为 request 的 policy container 的 connection allowlists。
-
对 allowlists 中的每个 allowlist 执行:
-
如果 allowlist 的 disposition 是 enforce, 则返回 blocked。
-
返回 allowed。
-
对 connection allowlists 中的每个 connection allowlist 执行:
-
如果 host host-matches connection allowlist,则继续。
-
给定 host、environment 和 connection allowlist, 报告违规。
-
如果 connection allowlist 的 disposition 是 enforce, 则返回 blocked。
-
-
返回 allowed。
-
令 allowlists 为 environment 的 policy container 的 connection allowlists。
-
对 allowlists 中的每个 allowlist 执行:
-
返回 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。
webrtc" (resource URL)、一个 environment (environment),以及一个
connection
allowlist (allowlist):
-
如果 allowlist 的 reporting endpoint 是
null,则返回。 -
令 violation 为一个新的
ConnectionAllowlistViolationReport, 初始化如下:
url-
environment 的 creation URL,并剥离以供报告使用。
connection-
如果 resource URL 是一个 URL,则为 resource URL,并剥离以供报告使用。
否则,为 resource URL。
allowlist-
一个新列表,包含对 allowlist 的 allowlist 中每个模式进行序列化的结果
disposition-
allowlist 的 disposition。
-
给定 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=]
-
令 origin 为从 response 的标头列表中获取 `
Allow-Connection-Allowlist-From` 的结果。 -
如果 origin 是
`*`,则返回 true。 -
如果使用 request 对请求源进行字节序列化的结果是 origin, 则返回 true; 否则返回 false。
注:这与 Allow-CSP-From 和 CORS 检查相对应:该标头会与嵌入方的 序列化请求源(或通配符)逐字节比较; 它完全不会被 解析(既不会被解析为URL,也不会被解析为源)。
4.3. 比较允许列表
结构体的相等性 尚未得到明确定义。此处,如果两个URL 模式 是从同一个序列化 字符串创建的,则它们相等。(另请参见 whatwg/infra#710。)[whatwg/infra 议题 #664]
注:这是一个(非严格)子集关系:a 中的每个 URL 模式都必须出现在 b 中, 但 a 和 b 可以相等。因此,断言与要求完全相同内容的响应 会满足该要求。
注:上述比较仅要求 响应断言的URL 模式存在于 要求中,而不会对这些模式进行任何语义分析,以确定它们是否在 逻辑上被包含。这是有意为之:鉴于URL 模式的语法, 判断一个模式是否完全包含 另一个模式是一个困难的问题,而保守的集合成员关系判断足以满足 开发者目前的用例。
注:由于被比较的允许列表会相对于
同一个URL进行解析,
因此response-origin 令牌
在二者中会以相同方式解析。
注:空的 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。
-
令 container 为 navigable 的容器。
-
令 parent required 为 container 的节点文档的策略容器的 所要求的连接 允许列表。
-
如果 container 是一个
iframe元素,该元素具有一个值(attribute value)不为空字符串的connectionAllowlist内容属性,并且给定 parent required, attribute value 是一个有效的 connectionAllowlist 属性值,则 返回 attribute value。 -
返回 parent required。
给定要求 parent required,如果以下所有条件均为 true,则称一个字符串(value)是一个有效的 connectionAllowlist 属性值:
-
value 不是空字符串。
-
value 的长度小于或等于 4096。
注:4096 是一个任意设定的上限, 它使序列化要求保持足够小,以便 携带在 `
Sec-Required-Connection-Allowlist` 请求标头中,并防止它在沿框架树向下继承时 无限制增长。具体数值并不重要。 -
parent required 为
null,或者 value至少与 parent required 同样严格。
注:框架可以收紧,但绝不能放宽
从其父级继承的要求:
仅当 connectionAllowlist 属性至少与父级的
要求同样严格时,才会采用该属性;否则会原样继承父级的要求。这与
[csp-embedded-enforcement] 的有效性
要求相对应,即
iframe
的 csp 属性必须被
嵌入方自身的要求所包含。
response-origin令牌会被视为
由字符串“response-origin”构建的字面URL 模式,而不是相对于
URL
进行解析。
注:此比较在导航时执行,
而不会将
response-origin 相对于
任何URL进行解析。由于
嵌入方的要求和框架自身的要求随后都会相对于同一个
每框架响应的URL进行解析,
因此在此处将该令牌视为与其自身相等是合理的,并且这样
可以避免依赖可能被跨源重定向更改的URL。该比较是保守的:如果框架的属性在语法上并不至少与其父级的
要求同样严格,则该框架只会原样继承该要求。
-
如果 list 为失败,则返回
null。 -
返回在给定 list、 response-url 和 disposition 的情况下,解析 Connection Allowlist 标头的结果。
null(requirement),返回
允许或
阻止:
-
如果 requirement 为
null,则返回允许。 -
令 required 为在给定 requirement、response 的URL 和 enforce 的情况下,解析 序列化的连接允许列表 要求的结果。
-
如果 required 为
null,则返回允许。注:格式错误的要求会被忽略, 而不会阻止框架。
-
令 header 为从 response 的标头列表中获取结构化字段值的结果,该字段 名为 `
Connection-Allowlist`, 类型为“list”。 -
令 asserted 为在给定 header、response 的URL 和 enforce 的情况下,解析 Connection Allowlist 标头的结果。
-
如果 asserted 不为
null,并且 asserted满足 required,则返回 允许。注:格式错误的 `
Connection-Allowlist` 会被解析为null,因此不会被视为 选择加入。 -
返回阻止。
注:本地方案文档
(例如 about:srcdoc 和 data:)没有
响应,也不会经过此检查;它们会直接继承
嵌入方的策略容器
(包括其中存储的要求)。参见§ 5.2 与 HTML 的集成。
5. 猴子补丁
5.1. 与 Fetch 的集成
我们将在 Fetch § 4.1 主 fetch 中添加一项阻止检查, 与用于相同目的的其他检查并列,以处理请求:
-
如果请求是否应因不良端口而被阻止、获取请求是否应作为混合内容而被阻止、请求是否应被内容安全策略阻止、 请求是否应被连接允许列表阻止、 或请求是否应被完整性策略 策略阻止返回已阻止,则将 response 设置为网络错误。
Fetch 还在较低层级定义了用于为不基于请求的 API 建立连接的算法。我们将接入解析 源和获取连接,以处理 DNS 预取、Web Transport 等:
我们将调用上述仅主机匹配算法,以确定是否有任何模式可能 允许连接到给定主机。如果没有,则解析失败。
Document
environment(默认为 null):
我们将在当前步骤 2 之前添加一项检查:
-
如果 environment 不为 null:
-
如果对 url、environment 和 allowlists 执行URL 是否应被 连接允许列表阻止返回已阻止, 则返回失败。
对 Fetch 的更改将 要求我们向低层算法的调用点传递额外信息,以确定应使用的允许列表以及用于 报告的上下文。或许更好的做法是让这些调用点自行执行检查。 我认为集中处理这些逻辑会更容易成功,但采用 分步处理的方式可能更简单。
5.2. 与 HTML 的集成
为了将上述内容集成到 HTML 中,我们将在策略容器 结构中添加一个新的 连接允许列表项,其中包含一个由连接允许列表组成的列表。通过向从获取响应创建策略 容器算法添加一个步骤来填充该项:
-
使用 response 和 result 解析 Integrity-Policy 标头。
- 将 result 的连接允许列表设置为 给定 response 时 解析响应的 Connection Allowlist的结果。
-
返回 result。
注:早期提示集成不需要进一步 更改,因为获取早期提示链接时已经使用了早期响应的 策略容器。
为了支持§ 4 嵌入式强制执行,我们遵循 [csp-embedded-enforcement] 为其 必需 CSP 所使用的模型,将嵌入器的要求从嵌入元素传递至 目标快照参数和导航参数,再传递到框架内 Document 的 策略容器。具体而言,按如下方式修补 HTML 和 Fetch:
-
向策略容器 结构 添加一个必需连接允许列表 项(定义于§ 4.4 获取并强制执行 要求)。
-
向目标快照参数结构添加一个必需连接允许列表项, 该项为
null或字符串。HTML 的 创建目标快照参数快照 算法将其设置为给定目标可导航对象时 确定必需 连接允许列表的结果。 -
向导航参数结构添加一个对应项,并由 导航、 通过获取创建导航参数以及 从 srcdoc 资源创建导航参数 将其设置为目标快照参数中的对应项。
-
通过获取创建导航参数 在创建请求后,当目标快照参数中的对应项不为
null时,将请求的标头列表中的 `Sec-Required-Connection-Allowlist` 标头设置为该项。 -
从获取 响应创建策略容器接受该要求作为附加 参数,并在填充 result 的连接允许列表后,运行下面的 应用必需连接 允许列表步骤。
注:与目标快照参数中已经携带的沙盒标志一样,该要求会在导航时从
iframe
容器创建快照,并在该可导航对象的每次
导航中生效,包括框架内 Document
自身发起的导航。因此,该约束会绑定同一可导航对象后续的导航,框架内内容无法通过自行导航到其他位置来规避它。
对 connectionAllowlist 内容属性的更改
会在可导航对象的下一次导航中生效,这与 sandbox 属性
的行为一致。(请注意,这与 referrerpolicy 不同,后者仅影响
容器自身发起的导航。)
注:嵌入器的连接允许列表会作为一个
附加条目添加到
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 链接类型的
获取并处理链接资源步骤中,
我们会将链接元素的节点文档传递给解析
源:
dns-prefetch 链接类型的
获取并处理链接资源步骤中,
步骤 4 更新如下:
5.4. 预连接
5.5. 与 WebRTC 集成
为了约束 WebRTC 连接,[webrtc] 可以在确定候选项是否在管理上被禁止时,调用 WebRTC 是否应被 Connection Allowlist 阻止 算法。
5.6. 与 Service Worker 集成
除了应用 Service Worker 自身策略容器的默认行为之外,
Service Worker 在使用 WindowClient
API 调用导航时,还需要应用自身的连接允许列表。
navigate()
和 openWindow(url)
算法都必须按如下方式修补:
-
如果对 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。以下几项 特性可防止其成为新的攻击面:
-
强制选择加入。嵌入方不能向不愿接受的跨源 内容施加策略。被框架嵌入的文档必须断言一个 `
Connection-Allowlist` 并使其满足 该要求,或者返回一个指定嵌入方的 `Allow-Connection-Allowlist-From` 标头。如果没有 选择加入(并且不是通过本地方案交付), 则框架会被阻止。这 对应于要求为iframe的csp属性选择加入的动机。 [csp-embedded-enforcement] -
继承时不得放宽。该要求会由后代框架 继承, 并且仅当后代自身的
connectionAllowlist属性满足 继承的要求时,才会采用该属性。因此,受约束的子框架无法通过嵌入 限制更宽松的孙级框架来逃避其约束。对于本地方案 文档,继承的允许列表 同样绝不会被嵌入式强制执行放宽。 -
保守接受。格式错误的已断言 `
Connection-Allowlist` 会被解析为null,且不算作选择加入;并且严格性比较是 一种保守的语法集合成员关系测试。因此,被框架嵌入的文档无法满足 其实际上并未达到的要求。
其请求不受嵌入方策略容器控制的上下文(例如 围栏框架)无法被此机制约束。当 嵌入方要求此类上下文采用连接允许列表时,该上下文必须被阻止,而不能允许其在 不受约束的情况下加载。
6.3. Service Worker
Service Worker会使允许列表相关情况变得复杂,正如 其他同源上下文一样。 因为它们具有一个与其管理的每个文档都不同的策略容器, 所以它们 完全可能响应由允许列表与 Service Worker 的允许列表不同的文档所发起的消息或请求。 此提案遵循其他策略的设计,允许 存在这些能力差异。
如果开发者希望约束 Service Worker 发出请求的能力,可以在交付 worker 脚本时一并交付允许列表,但需要确保该允许列表是其可能服务的任何文档的 允许列表的超集。
6.4. DNS
连接允许列表旨在降低通过 DNS 查询泄露数据的风险,它会修改 Fetch 的
解析源算法,使其在发送
DNS 请求之前检查允许列表。这不仅会处理因使用发送请求的元素和 API
而产生的隐式查询
(img
标签、fetch()
等),还会处理由
dns-prefetch
等机制触发的显式查询。
尽管如此,允许列表依赖基于 URL 的匹配来确定给定端点是否 可接受。它不能防御 DNS 操纵,也不会限制请求中涉及的 DNS 服务器 (包括 DNS 解析的递归性质可能与多个服务器 通信),也不会限制 CNAME 等结构导致对 允许列表之外域名进行额外查询。同样,允许列表无法保证 DNS 指向的服务器就是你所预期的服务器。
开发者应依赖经过身份验证的 连接来降低 DNS 劫持和/或重绑定的风险:仅允许安全协议将使控制 DNS 的攻击者更难将流量转移到任意端点,因为 TLS 握手会要求 持有包含相关名称的证书。
我们是否应将 允许列表的模式限制为表示安全协议的模式?或者对于非安全源完全不支持 该标头?
6.5. postMessage(...)
此提案完全关注网络连接,这可能会令那些
期望通过
postMessage(message, options)、
MessageChannel、
BroadcastChannel
等显式通信通道进行的通信
也会被涵盖的开发者感到意外。将模型扩展为同时包含这些通道可能是合理的,因为它们都符合
一种可以与允许列表进行有意义比较的基于源的模型。
6.6. 重定向
默认情况下,连接允许列表会阻止所有重定向。这是一种保守立场, 旨在防止通过开放重定向或其他服务器端重定向机制泄露数据。 如果对文档强制执行允许列表,则任何产生重定向的请求都会被阻止, 除非允许列表显式选择允许重定向。
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/)也将被
允许。
最后,为了实现向前兼容,未知令牌将被视为 allow:
Connection-Allowlist: ("https://api.example");redirects=some-future-policy
在此情况下,redirects 参数存在,但其值
some-future-policy 未知。
用户代理会将其视为 allow,并且重定向将被允许。
这使我们
将来能够引入其他行为,而不会破坏现有
站点。
6.7. 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”之类模式的允许列表不会产生预期效果,
因为资源请求永远不会匹配该模式。