当来自用户控制的来源(例如用户名或从 URL 片段获取的重定向 URL)的数据到达接收器时,就会发生基于 DOM 的跨站脚本 (DOM XSS),接收器是诸如 eval()
之类的函数或诸如 .innerHTML
之类的属性 setter,它们可以执行任意 JavaScript 代码。
DOM XSS 是最常见的 Web 安全漏洞之一,开发团队经常会在其应用程序中意外引入它。Trusted Types 通过默认保护危险的 Web API 函数,为您提供工具来编写、安全审查和保持应用程序免受 DOM XSS 漏洞的侵害。Trusted Types 可以作为 polyfill 用于尚不支持它们的浏览器。
背景
多年来,DOM XSS 一直是最普遍和最危险的 Web 安全漏洞之一。
跨站脚本攻击有两种。一些 XSS 漏洞是由服务器端代码不安全地创建构成网站的 HTML 代码引起的。另一些则在客户端有根本原因,JavaScript 代码在客户端使用用户控制的内容调用危险函数。
要防止服务器端 XSS,请勿通过连接字符串来生成 HTML。请改用安全的上下文自动转义模板库,以及基于 nonce 的内容安全策略,以进行额外的错误缓解。
现在,浏览器还可以通过使用 Trusted Types 来帮助防止基于客户端 DOM 的 XSS。
API 简介
Trusted Types 通过锁定以下有风险的接收器函数来工作。您可能已经认识到其中的一些,因为浏览器供应商和 Web 框架 出于安全原因已经引导您远离使用这些功能。
- 脚本操作:
<script src>
和设置<script>
元素的文本内容。 - 从字符串生成 HTML:
- 执行插件内容:
- 运行时 JavaScript 代码编译:
eval
setTimeout
setInterval
new Function()
Trusted Types 要求您在将数据传递给这些接收器函数之前对其进行处理。仅使用字符串会失败,因为浏览器不知道数据是否可信
anElement.innerHTML = location.href;
要表明数据已安全处理,请创建一个特殊对象 - Trusted Type。
anElement.innerHTML = aTrustedHTML;
TrustedHTML
对象用于期望 HTML 代码段的接收器。还有用于其他敏感接收器的 TrustedScript
和 TrustedScriptURL
对象。
Trusted Types 显着减少了应用程序的 DOM XSS 攻击面。它简化了安全审查,并允许您在运行时在浏览器中强制执行编译、linting 或捆绑代码时完成的基于类型的安全检查。
如何使用 Trusted Types
准备内容安全策略违规报告
您可以部署报告收集器,例如开源的 reporting-api-processor 或 go-csp-collector,或者使用商业等效项之一。您还可以使用 ReportingObserver 添加自定义日志记录和调试浏览器中的违规行为
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
或通过添加事件侦听器
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
添加仅报告 CSP 标头
将以下 HTTP 响应标头添加到您要迁移到 Trusted Types 的文档
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
现在,所有违规行为都报告给 //my-csp-endpoint.example
,但网站继续工作。下一节将解释 //my-csp-endpoint.example
的工作原理。
识别 Trusted Types 违规行为
从现在开始,每次 Trusted Types 检测到违规行为时,浏览器都会向配置的 report-uri
发送报告。例如,当您的应用程序将字符串传递给 innerHTML
时,浏览器会发送以下报告
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
这表示在 https://my.url.example/script.js
的第 39 行,使用以 <img src=x
开头的字符串调用了 innerHTML
。此信息应帮助您缩小哪些代码部分可能引入 DOM XSS 并需要更改的范围。
修复违规行为
有几种修复 Trusted Type 违规行为的选项。您可以删除有问题的代码、使用库、创建 Trusted Type 策略,或者作为最后的手段,创建默认策略。
重写有问题的代码
可能不再需要不合规的代码,或者可以重写它而无需导致违规的函数
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
使用库
某些库已经生成 Trusted Types,您可以将其传递给接收器函数。例如,您可以使用 DOMPurify 来清理 HTML 代码段,删除 XSS 有效负载。
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify 支持 Trusted Types 并返回包装在 TrustedHTML
对象中的清理后的 HTML,以便浏览器不会生成违规行为。
创建 Trusted Type 策略
有时您无法删除导致违规的代码,也没有库可以清理值并为您创建 Trusted Type。在这些情况下,您可以自己创建 Trusted Type 对象。
首先,创建一个 策略。策略是 Trusted Types 的工厂,它对其输入强制执行某些安全规则
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
此代码创建一个名为 myEscapePolicy
的策略,该策略可以使用其 createHTML()
函数生成 TrustedHTML
对象。定义的规则 HTML 转义 <
字符以防止创建新的 HTML 元素。
像这样使用策略
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
使用默认策略
有时您无法更改有问题的代码,例如,如果您从 CDN 加载第三方库。在这种情况下,请使用 默认策略
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
名为 default
的策略用于在仅接受 Trusted Type 的接收器中使用字符串的任何地方。
切换到强制执行内容安全策略
当您的应用程序不再产生违规行为时,您可以开始强制执行 Trusted Types
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
现在,无论您的 Web 应用程序有多么复杂,唯一可能引入 DOM XSS 漏洞的是您的策略之一中的代码,并且您可以通过限制策略创建来进一步锁定它。