请注意,此文档可能有勘误表。
此规范的英文版本是唯一规范版本。非规范性的翻译版本也可能有。
Copyright © 2014-2016 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use rules apply.
本规范定义了一种机制,用户代理可通过该机制验证获取的资源是否未经预期的操纵而传递。
本节描述了本文件在发布时的状态。其他文件可能会取代本文件。可以在W3C 技术报告 索引找到当前 W3C 的出版物和此技术报告的最新版本,网址为 http://www.w3.org/TR/。
本文件的变更列表可在 https://github.com/w3c/webappsec-subresource-integrity 找到。
本文件由Web 应用安全工作组作为推荐发布。如果您希望对此文件发表意见,请将其发送至
public-webappsec@w3.org (订阅,
存档),并在电子邮件主题的开头使用[SRI]
。欢迎所有意见。
请参阅工作组的实施报告。
本文件已由 W3C 成员、软件开发者及其他 W3C 小组和相关方进行审核,并获得了总监的认可,作为 W3C 推荐。本文件是稳定的,可以用作参考资料或在其他文件中引用。W3C 制定推荐标准的作用是引起对该规范的关注,并推动其广泛部署。这增强了Web的功能性和互操作性。
W3C 预计本推荐中规定的功能不会受到Fetch 的变更影响。工作组将继续跟踪Fetch规范并记录影响本规范的问题。
本文件由依据2004年2月5日 W3C 专利政策 运营的小组制作。 W3C 维护了与该小组交付成果相关的任何专利披露的公开列表;该页面还包含披露专利的说明。任何个人如果确知某专利包含 必要声明,必须根据W3C专利政策第6节披露信息。
本文件受2015年9月1日 W3C 流程文件的管理。
本节是非规范性的。
网络上的站点和应用很少仅由单一来源的资源组成。例如,作者从各种服务和内容交付网络(CDN)中提取脚本和样式,必须信任所交付的内容确实是他们预期加载的内容。如果攻击者通过DNS欺骗或其他手段,诱使用户从恶意服务器下载内容,作者将无能为力。同样,攻击者如果能替换CDN服务器上的文件,也可以注入任意内容。
通过安全通道交付资源可以减轻这种风险:通过TLS、HSTS和固定公钥,用户代理可以相当确定它确实在与它认为的服务器通信。然而,这些机制仅验证了服务器,不验证内容。拥有服务器访问权限的攻击者(或管理员)可以随意操纵内容。理想情况下,作者不仅能够固定服务器的密钥,还能固定内容,确保只加载和执行特定资源的精确表示。
本文档定义了这样一个验证方案,扩展了两个HTML元素,添加了一个包含作者期望加载的资源表示的加密哈希的integrity
属性。例如,作者可能希望从共享服务器而不是自己的来源加载某个框架。指定预期的SHA-384哈希为https://example.com/example-framework.js
是Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7
,这意味着用户代理可以在执行JavaScript之前,验证从该URL加载的数据是否与该预期哈希匹配。这种完整性验证显著降低了攻击者替换恶意内容的风险。
此示例可以通过在script
元素中添加哈希传达给用户代理,如下所示:
<script src="https://example.com/example-framework.js"
integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7"
crossorigin="anonymous"></script>
当然,脚本并不是唯一可以从完整性验证中受益的响应类型。此处指定的方案也适用于link
,未来的版本可能会扩展到更多的类型。
第三方服务的妥协不应自动意味着每个包含其脚本的站点都受到威胁。内容作者将有机制指定他们加载的内容期望,例如,他们可以加载特定的脚本,而不是碰巧具有特定URL的任何脚本。
验证机制应具有错误报告功能,通知作者接收到的响应无效。
作者希望使用内容交付网络(CDN)提高全球用户的性能。然而,确保CDN服务器只交付作者期望的代码非常重要。为了减轻CDN妥协(或意外的恶意行为)导致站点发生不利变化的风险,以下完整性元数据被添加到页面的link
元素中:
<link rel="stylesheet" href="https://site53.example.net/style.css"
integrity="sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB"
crossorigin="anonymous">
作者希望包含第三方分析服务提供的JavaScript。为了确保只有经过仔细审核的代码被执行,作者为脚本生成了完整性元数据,并将其添加到script
元素中:
<script src="https://analytics-r-us.example.com/v1.0/include.js"
integrity="sha384-MBO5IDfYaE6c6Aao94oZrIOiC6CGiSN2n4QUbHNPhzk5Xhm0djZLQqTpL0HzTUxk"
crossorigin="anonymous"></script>
用户代理希望确保在高权限HTML上下文(例如,浏览器的新标签页)中运行的JavaScript代码在显示之前未被篡改。完整性元数据减轻了在这些页面的高权限上下文中运行被修改的JavaScript的风险。
除了标记为非规范的章节外,本规范中的所有编写指南、图表、示例和注释都是非规范性的。本规范中的其他内容均为规范性内容。
关键词 MAY、MUST 和 SHOULD 按照 [RFC2119] 中的描述进行解释。
以算法或特定步骤表达的一致性要求可以以任何方式实现,只要最终结果等效即可。特别是,本规范中定义的算法旨在便于理解,而不是为了性能。建议实现者进行优化。
本节定义了文档中使用的几个术语。
术语摘要指的是对任意数据块执行加密哈希函数后生成的base64编码结果。
术语源在源规范中定义。[RFC6454]
表示数据和内容编码由RFC7231,第3节定义。[RFC7231]
Base64编码定义在RFC 4648,第4节中。[RFC4648]
SHA-256、SHA-384和SHA-512是由NIST定义的SHA-2加密哈希函数集的一部分,参见“FIPS PUB 180-4: Secure Hash Standard (SHS)”。
本文件中使用的增强型巴科斯-瑙尔形式(ABNF)符号在RFC5234中规定。[ABNF]
WSP
(空白字符)在HTML 5规范的“2.4.1
公共解析器惯例”中定义为White_Space characters
。
此处指定的完整性验证机制归结为为资源生成足够强的加密摘要,并将该摘要传输给用户代理,以便其用于验证响应。
为了验证响应的完整性,用户代理需要完整性元数据作为请求的一部分。此元数据包含以下信息:
必须提供哈希函数和摘要,才能验证响应的完整性。
目前尚未定义任何选项。然而,规范的未来版本可能会定义选项,例如MIME类型 [MIMETYPE]。
此元数据必须按照内容安全策略第2级规范第4.2节中的hash-source
(不带单引号)相同的格式进行编码。
例如,给定一个仅包含字符串alert(\'Hello, world.\');
的脚本资源,作者可能会选择SHA-384作为哈希函数。
结果的base64编码摘要为H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO
。可以如下编码:
sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO
可以使用许多工具生成摘要。例如,OpenSSL相当常见。本节中的示例是通过以下命令行生成的结果:
echo -n "alert('Hello, world.');" | openssl dgst -sha384 -binary | openssl base64 -A
符合要求的用户代理必须支持SHA-256、SHA-384和SHA-512加密哈希函数,用作请求的完整性元数据的一部分,并且可以支持其他哈希函数。
用户代理应当拒绝支持已知的弱哈希函数,如MD5或SHA-1,并且应当将支持的哈希函数限制为已知的抗碰撞哈希函数。此外,用户代理应当定期重新评估其支持的哈希函数,并弃用那些已变得不安全的函数。参见哈希碰撞攻击。
可以将多个完整性元数据集与单个资源关联,以便在面对未来加密发现时提供灵活性。例如,前一节中描述的资源可以通过以下任一哈希表达式进行描述:
sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd
sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw==
作者可以选择同时指定两者,例如:
<script src="hello_world.js"
integrity="sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd
sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw=="
crossorigin="anonymous"></script>
在这种情况下,用户代理将选择列表中最强的哈希函数,并使用该元数据来验证响应(如下面“解析元数据”和“从集合中获取最强的元数据”算法中所述)。
当哈希函数被确定为不安全时,用户代理应当弃用并最终移除使用不安全哈希函数的完整性验证支持。用户代理可以检查基于已弃用函数的响应的有效性。
为了让作者能够在不受旧版用户代理阻碍的情况下切换到更强的哈希函数,使用不支持的哈希函数进行验证的行为与未提供完整性值时相同(参见下文的“响应是否匹配元数据列表”算法)。鼓励作者使用强哈希函数,并在更强的哈希函数可用时开始迁移。
用户代理必须提供一种机制,用于确定两个哈希函数的相对优先级,如果优先级相等,则返回空字符串。也就是说,如果用户代理实现了一个类似于getPrioritizedHashFunction(a,
b)的函数,它将返回用户代理认为最具抗碰撞性的哈希函数。例如,getPrioritizedHashFunction('sha256',
'sha512')
将返回'sha512'
,而getPrioritizedHashFunction('sha256',
'sha256')
将返回空字符串。
getPrioritizedHashFunction是一个内部实现细节。它不是实现者提供给Web应用的API。它仅用于简化本文档中的算法描述。
为了减轻攻击者通过完整性检查暴力破解值来跨源读取数据的能力,只有当响应是同源的或通过跨源资源共享明确授予加载源访问权限时,响应才有资格进行此类检查 [CORS]。
如RFC6454,第4节所述,某些用户代理对每个文件URI使用全局唯一标识符。这意味着通过file
方案URL访问的资源可能不符合完整性检查的资格。
处于安全上下文(例如,通过HTTPS传输的文档)并不是使用完整性验证的必要条件。由于资源完整性仅是应用级别的安全工具,它不会改变用户代理的安全状态,因此安全上下文不是必须的。然而,如果在非安全上下文中使用完整性验证(例如,通过HTTP传输的文档),作者应意识到完整性验证根本不提供任何安全保证。因此,作者应仅在安全上下文中传递完整性元数据。有关更多讨论,请参见非安全上下文仍然不安全。
以下算法详细说明了这些限制:
此算法接受一个字符串,并返回无元数据
或一组用户代理能够理解的有效哈希表达式。
getPrioritizedHashFunction(currentAlgorithm, newAlgorithm)
的结果为空字符串,将项添加到结果中。如果结果为新算法,将最强设置为项,将结果设置为空集合,并将项添加到结果中。
无元数据
,返回true
。false
。
true
。true
。false
。该算法允许用户代理接受多个有效的强哈希函数。例如,开发者可以编写如下的script
元素:
<script src="https://example.com/example-framework.js"
integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7
sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB"
crossorigin="anonymous"></script>
这将允许用户代理接受两个不同的内容有效负载,一个匹配第一个SHA384哈希值,另一个匹配第二个SHA384哈希值。
用户代理可以允许用户通过用户偏好、书签、第三方对用户代理的扩展等机制修改该算法的结果。例如,由类似HTTPS Everywhere的扩展生成的重定向可以正确加载和执行,即使资源的HTTPS版本与HTTP版本不同。
如果响应不符合完整性验证的资格,该算法返回false
,因为子资源完整性(SRI)需要CORS,尝试在没有CORS的情况下使用它是一个逻辑错误。此外,用户代理应当在开发者控制台报告警告信息,以解释该失败。
各种HTML元素会导致请求将资源嵌入到文档中,或在其上下文中执行。为了支持其中一些元素的完整性元数据,integrity
属性被添加到link
和script
元素的内容属性列表中。
一个对应的integrity
IDL属性,它反映了每个元素的integrity
内容属性的值,被添加到HTMLLinkElement
和HTMLScriptElement
接口中。
本规范的未来修订版本可能会包括对所有可能子资源的完整性支持,即a
、audio
、embed
、
iframe
、img
、link
、object
、script
、source
、
track
和video
元素。
integrity
属性
integrity
属性表示元素的完整性元数据。属性的值必须是空字符串或至少一个有效的元数据,如以下ABNF语法所描述:
integrity-metadata = *WSP hash-with-options *( 1*WSP hash-with-options ) *WSP / *WSP
hash-with-options = hash-expression *("?" option-expression)
option-expression = *VCHAR
hash-algo = <hash-algo production from [Content Security Policy Level 2, section 4.2]>
base64-value = <base64-value production from [Content Security Policy Level 2, section 4.2]>
hash-expression = hash-algo "-" base64-value
integrity
IDL属性必须反映integrity
内容属性的值。
option-expression
与每个hash-expression
关联,并且仅应用于其前面的hash-expression
。
为了确保用户代理完全向未来的选项兼容,用户代理必须忽略所有无法识别的option-expression
。
请注意,虽然option-expression
已在语法中保留,但尚未定义任何选项。很可能在未来的版本中会为选项定义更具体的语法,因此在此定义为尽可能广泛。
partial interface HTMLLinkElement {
attribute DOMString integrity;
};
integrity
类型为DOMStringintegrity
partial interface HTMLScriptElement {
attribute DOMString integrity;
};
integrity
类型为DOMStringintegrity
用户代理将拒绝渲染或执行未通过完整性检查的响应,而是返回Fetch中定义的网络错误 [FETCH]。
在完整性检查失败时,会触发error
事件。希望提供规范备用资源(例如,未从CDN提供的资源,可能来自二级、受信任但较慢的源)的开发人员可以捕获此error
事件,并提供适当的处理程序以将失败的资源替换为另一个资源。
link
元素用于样式表每当用户代理尝试获取由link
元素指向的资源时,如果其rel
属性包含stylesheet
关键字,请修改步骤4为:
对生成的绝对URL进行潜在的启用CORS的获取,模式为元素crossorigin内容属性的当前状态,源为link元素文档的源,默认源行为设置为污染,并将请求的完整性元数据设置为元素integrity
属性的值。
script
元素
将HTML5中“准备脚本”算法的步骤14.1替换为:
src
属性的值,请求的关联完整性元数据为元素integrity
属性的值。优化代理和其他修改响应的中间服务器必须确保与这些响应相关的摘要与新内容保持同步。一种选择是确保与资源相关的完整性元数据已更新。另一种选择是仅传送页面作者请求完整性验证的资源的规范版本。
为了帮助通知中间服务器,提供资源的服务器应当与资源一起发送带有Cache-Control
标头,并将其值设置为no-transform
。
本节为非规范性内容。
由非安全上下文(例如HTTP页面)传递的完整性元数据仅能保护原点免受托管外部资源的服务器被入侵的情况。网络攻击者可以在传输过程中更改摘要(或完全删除它,或对文档做任何其他更改),正如他们可以更改哈希试图验证的响应一样。因此,建议作者仅将完整性元数据传递到安全上下文。另见保护网络安全。
摘要的强度取决于用于生成它们的哈希函数。建议用户代理拒绝支持已知弱的哈希函数,并将支持的算法限制为已知的抗碰撞算法。不推荐的哈希函数示例包括MD5和SHA-1。在撰写本文时,SHA-384是一个不错的基准。
此外,建议用户代理定期重新评估其支持的哈希函数,并弃用已被证明不安全的函数。随着时间的推移,哈希函数可能被证明比预期的要弱得多,甚至在某些情况下被破解,因此用户代理需要关注这些进展。
本规范要求跨源请求的CORS设置属性存在于受完整性保护的跨源请求中。如果省略了这一要求,攻击者可能会违反同源策略,并确定跨源资源是否具有特定内容。
攻击者可能会尝试使用已知摘要加载资源,并观察加载失败情况。如果加载失败,攻击者可以推断出响应与哈希不匹配,从而获得其内容的一些信息。例如,这可能会揭示用户是否已登录到某个特定服务。
此外,攻击者还可以通过蛮力破解特定值来攻击一个基本静态的资源。考虑如下的JSON响应:
{'status': 'authenticated', 'username': 'admin'}
攻击者可以为响应中的常见用户名预计算哈希,并在反复尝试加载文档时指定这些哈希。成功的加载将确认攻击者已正确猜出了用户名。
本内容的许多部分深受Gervase Markham的Link Fingerprints概念以及WHATWG的Link Hashes的启发。
特别感谢Google公司的Mike West对本规范初始版本的宝贵贡献。此外,Brad Hill、Anne van Kesteren、Jonathan Kingston、Mark Nottingham、Dan Veditz、Eduardo Vela、Tanvi Vyas和Michal Zalewski也提供了非常宝贵的反馈意见。