Trusted Types API
注意:此功能在 Web Workers 中可用。
Trusted Types API 为 Web 开发者提供了一种方法,可以确保在将输入传递给可能执行该输入的 API 之前,该输入已通过用户指定的转换函数。这有助于防止客户端 跨站脚本 (XSS) 攻击。最常见的转换函数是 净化输入。
概念与用法
客户端或基于 DOM 的 XSS 攻击发生在攻击者构造的数据被传递给将该数据作为代码执行的浏览器 API 时。这些 API 被称为注入接收器。
Trusted Types API 区分三种类型的注入接收器
- HTML 接收器:将输入解释为 HTML 的 API,例如
Element.innerHTML或document.write()。这些 API 可能会执行 JavaScript,如果 JavaScript 嵌入在 HTML 中,例如在<script>标签或事件处理程序属性中。 - JavaScript 接收器:将输入解释为 JavaScript 的 API,例如
eval()或HTMLScriptElement.text。 - JavaScript URL 接收器:将输入解释为脚本 URL 的 API,例如
HTMLScriptElement.src。
防止基于 DOM 的 XSS 攻击的主要方法之一是确保在将输入传递给注入接收器之前将其进行安全处理。
在 Trusted Types API 中,开发者定义一个策略对象,其中包含转换将要进入注入接收器的输入以使其安全的方法。策略可以为不同类型的接收器定义不同的方法。
- 对于 HTML 接收器,转换函数通常会对输入进行净化,例如使用像 DOMPurify 这样的库。
- 对于 JavaScript 和 JavaScript URL 接收器,策略可以完全禁用这些接收器,或者允许某些预定义输入(例如,特定的 URL)。
Trusted Types API 将确保在将输入传递到接收器之前,会通过相应的转换函数进行处理。
也就是说,该 API 使您能够在一个地方定义策略,然后确信任何传递到注入接收器的数据都已通过该策略进行处理。
备注
Trusted Types API 本身不提供策略或任何转换函数:开发者定义自己的策略,其中包含他们希望应用的转换。
该 API 有两个主要部分
- 一个 JavaScript API 使开发者能够在将数据传递给注入接收器之前对其进行净化。
- 两个 CSP 指令用于强制执行和控制 JavaScript API 的使用。
Trusted Types JavaScript API
在 Trusted Types API 中
- 全局属性
trustedTypes,在Window和Worker上下文中都可用,用于创建TrustedTypePolicy对象。 - 一个
TrustedTypePolicy对象用于创建受信任的类型对象:它将通过转换函数来处理数据。 - 受信任的类型对象表示已通过策略处理的数据,因此可以安全地传递给注入接收器。有三种受信任的类型,对应于不同类型的注入接收器:
TrustedHTML用于传递给将数据渲染为 HTML 的接收器。TrustedScript用于传递给将数据作为 JavaScript 执行的接收器。TrustedScriptURL用于传递给将数据解析为脚本 URL 的接收器。
使用此 API,而不是将字符串传递给 innerHTML 这样的注入接收器,您可以使用 TrustedTypePolicy 从字符串创建 TrustedHTML 对象,然后将其传递到接收器,这样就可以确保字符串已通过转换函数。
例如,此代码创建了一个 TrustedTypePolicy,该策略可以通过 DOMPurify 库净化输入字符串来创建 TrustedHTML 对象。
const policy = trustedTypes.createPolicy("my-policy", {
createHTML: (input) => DOMPurify.sanitize(input),
});
接下来,您可以使用此 policy 对象创建一个 TrustedHTML 对象,并将该对象传递到注入接收器。
const userInput = "<p>I might be XSS</p>";
const element = document.querySelector("#container");
const trustedHTML = policy.createHTML(userInput);
element.innerHTML = trustedHTML;
使用 CSP 强制执行受信任类型
上面描述的 API 使您能够净化数据,但它不能确保您的代码永远不会将输入直接传递给注入接收器:也就是说,它不会阻止您将字符串传递到 innerHTML。
为了强制始终传递受信任的类型,您需要在您的 CSP 中包含 require-trusted-types-for 指令。设置此指令后,将字符串传递到注入接收器将导致 TypeError 异常。
const userInput = "<p>I might be XSS</p>";
const element = document.querySelector("#container");
element.innerHTML = userInput; // Throws a TypeError
此外,trusted-types CSP 指令可用于控制您的代码可以创建哪些策略。当您使用 trustedTypes.createPolicy() 创建策略时,会传递策略的名称。trusted-types CSP 指令列出了可接受的策略名称,因此如果 createPolicy() 传递了未在 trusted-types 中列出的名称,它将抛出异常。这可以防止您的 Web 应用程序中的某些代码创建您未预期的策略。
默认策略
在 Trusted Types API 中,您可以定义一个默认策略。这有助于您查找代码中仍然将字符串传递到注入接收器的位置,以便您可以重写代码以创建并传递受信任的类型。
如果您创建一个名为 "default" 的策略,并且您的 CSP 强制使用受信任的类型,那么传递到注入接收器的任何字符串参数都将自动传递给此策略。例如,假设我们创建了一个如下策略:
trustedTypes.createPolicy("default", {
createHTML(value) {
console.log("Please refactor this code");
return sanitize(value);
},
});
使用此策略,如果您的代码将字符串分配给 innerHTML,浏览器将调用策略的 createHTML() 方法并将其结果分配给接收器。
const userInput = "<p>I might be XSS</p>";
const element = document.querySelector("#container");
element.innerHTML = userInput;
// Logs "Please refactor this code"
// Assigns the result of sanitize(userInput)
如果默认策略返回 null 或 undefined,那么当浏览器将结果分配给接收器时会抛出 TypeError。
trustedTypes.createPolicy("default", {
createHTML(value) {
console.log("Please refactor this code");
return null;
},
});
const userInput = "<p>I might be XSS</p>";
const element = document.querySelector("#container");
element.innerHTML = userInput;
// Logs "Please refactor this code"
// Throws a TypeError
注意: 建议仅在您从传递输入到注入接收器的旧代码迁移到显式使用受信任类型的代码时使用默认策略。
跨浏览器对受信任类型的支持
Trusted Types API 尚未在所有现代浏览器中可用,但由于 W3C 创建的兼容性辅助工具,今天在任何地方都可以使用。
- 完整 polyfill 定义了 JavaScript API,尝试从当前文档推断 CSP,并基于推断的 CSP 强制使用受信任的类型。
- 仅 API polyfill 仅定义了 JavaScript API,并且不包含使用 CSP 强制使用受信任类型的能力。
除了这两个 polyfill 之外,W3C 还提供了所谓的迷你 polyfill,我们将在下面更详细地解释。
请注意,只要您在支持 CSP 强制执行的浏览器上测试过您的代码,那么您就不需要在其他浏览器上使用上面的完整 polyfill —您可以使用仅 API polyfill 或迷你 polyfill 来获得相同的优势。
这是因为强制执行会迫使您重构代码,以确保所有数据在传递给注入接收器之前都已通过 Trusted Types API(因此已通过净化函数)。如果您随后在没有强制执行的另一浏览器中运行重构后的代码,它仍然会经过相同的代码路径,并为您提供相同的保护。
Trusted Types 迷你 polyfill
在本节中,我们将探讨 trusted types 迷你 polyfill 如何保护网站,即使它根本不支持 trusted types。
trusted types 迷你 polyfill 就是这个:
if (typeof trustedTypes === "undefined")
trustedTypes = { createPolicy: (n, rules) => rules };
它提供了 trustedTypes.createPolicy() 的实现,该实现仅返回它接收到的 policyOptions 对象。policyOptions 对象定义了数据的净化函数,这些函数应返回字符串。
在此迷你 polyfill 的基础上,假设我们创建一个策略:
const policy = trustedTypes.createPolicy("my-policy", {
createHTML: (input) => DOMPurify.sanitize(input),
});
在支持 trusted types 的浏览器中,这将返回一个 TrustedTypePolicy,当我们在调用 policy.createHTML() 时,它将创建一个 TrustedHTML 对象。然后可以将 TrustedHTML 对象传递给注入接收器,并且我们可以强制接收器接收的是一个受信任的类型,而不是一个字符串。
在不支持 trusted types 的浏览器中,此代码将返回一个具有 createHTML() 函数的对象,该函数会净化其输入并将其作为字符串返回。然后可以将净化后的字符串传递给注入接收器。
const userInput = "I might be XSS";
const element = document.querySelector("#container");
const trustedHTML = policy.createHTML(userInput);
// In supporting browsers, trustedHTML is a TrustedHTML object.
// In non-supporting browsers, trustedHTML is a string.
element.innerHTML = trustedHTML;
// In supporting browsers, this will throw if trustedHTML
// is not a TrustedHTML object.
无论哪种方式,注入接收器都会收到净化后的数据,并且由于我们在支持的浏览器中可以强制使用该策略,因此我们知道在不支持的浏览器中,此代码路径也会经过净化函数。
接口
TrustedHTML-
表示要插入到注入接收器中的字符串,该接收器将把它渲染为 HTML。
TrustedScript-
表示要插入到注入接收器中的字符串,该注入接收器可能导致脚本被执行。
TrustedScriptURL-
表示要插入到注入接收器中的字符串,该注入接收器将将其解析为外部脚本资源的 URL。
TrustedTypePolicy-
定义用于创建上述受信任类型对象的函数。
TrustedTypePolicyFactory-
创建策略并验证受信任类型对象实例是否通过其中一个策略创建。
示例
在下面的示例中,我们使用 TrustedTypePolicyFactory.createPolicy() 创建了一个将用于创建 TrustedHTML 对象的策略。然后,我们可以使用 TrustedTypePolicy.createHTML() 创建一个要插入到文档中的净化后的 HTML 字符串。
净化后的值然后可以与 Element.innerHTML 一起使用,以确保不会注入新的 HTML 元素。
<div id="myDiv"></div>
const escapeHTMLPolicy = trustedTypes.createPolicy("myEscapePolicy", {
createHTML: (string) =>
string
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/"/g, """)
.replace(/'/g, "'"),
});
let el = document.getElementById("myDiv");
const escaped = escapeHTMLPolicy.createHTML("<img src=x onerror=alert(1)>");
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped;
有关此示例的更多信息,以及发现其他净化输入的方法,请参阅文章 使用 Trusted Types 防止基于 DOM 的跨站脚本漏洞。
规范
| 规范 |
|---|
| Trusted Types |
浏览器兼容性
加载中…