当来自用户控制的来源(例如用户名或从网址 fragment 中获取的重定向网址)的数据到达接收器(即可以执行任意 JavaScript 代码的函数,如 eval()
或属性 setter,如 .innerHTML
)时,就会发生基于 DOM 的跨站脚本攻击 (DOM XSS)。
DOM XSS 是最常见的 Web 安全漏洞之一,开发团队经常会在应用中意外引入该漏洞。 借助可信类型,您可以默认情况下使危险的 Web API 函数安全,从而获得相关工具来编写、安全审核和维护不含 DOM XSS 漏洞的应用。对于尚不支持可信类型的浏览器,可信类型可作为 polyfill 提供。
背景
多年来,DOM XSS 一直是最普遍、最危险的 Web 安全漏洞之一。
跨站脚本攻击有两种类型。某些 XSS 漏洞是由不安全地创建构成网站的 HTML 代码的服务器端代码引起的。其他漏洞的根本原因在于客户端,即 JavaScript 代码使用用户控制的内容调用危险函数。
为防止服务器端 XSS,请勿通过连接字符串来生成 HTML。请改用安全的上下文自动转义模板库,并搭配基于随机数的 Content Security Policy,以进一步缓解 bug。
现在,浏览器还可以通过使用可信类型来帮助防范基于客户端 DOM 的 XSS 攻击。
API 简介
Trusted Types 通过锁定以下有风险的接收器函数来发挥作用。您可能已经认识到其中一些功能,因为浏览器供应商和Web 框架出于安全考虑,已经引导您避免使用这些功能。
- 脚本操作:
<script src>
和设置<script>
元素的文本内容。 - 根据字符串生成 HTML:
- 执行插件内容:
- 运行时 JavaScript 代码编译:
eval
setTimeout
setInterval
new Function()
Trusted Types 要求您先处理数据,然后再将其传递给这些接收器函数。仅使用字符串会失败,因为浏览器不知道数据是否可信:
anElement.innerHTML = location.href;
为了表明数据已得到安全处理,请创建一个特殊对象(即受信任的类型)。
anElement.innerHTML = aTrustedHTML;
TrustedHTML
对象,用于需要 HTML 代码段的接收器。还有用于其他敏感接收器的 TrustedScript
和 TrustedScriptURL
对象。
Trusted Types 可显著缩小应用的 DOM XSS 攻击面。它简化了安全审核,并允许您在运行时在浏览器中强制执行在编译、Lint 或捆绑代码时完成的基于类型的安全检查。
如何使用可信类型
准备接收内容安全政策违规报告
您可以部署报告收集器,例如开源的 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 响应标头添加到要迁移到可信类型的文档中:
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"
}
}
这表示在第 39 行的 https://my.url.example/script.js
中,innerHTML
是使用以 <img src=x
开头的字符串调用的。这些信息应有助于您缩小代码范围,找出可能引入 DOM XSS 的部分并进行更改。
解决违规问题
您可以通过以下几种方法来修正 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 支持可信类型,并返回封装在 TrustedHTML
对象中的经过清理的 HTML,以便浏览器不会生成违规行为。
创建受信任的类型政策
有时,您无法移除导致违规的代码,也没有可用于清理值并为您创建可信类型的库。在这些情况下,您可以自行创建 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
的政策。
切换到强制执行内容安全政策
当您的应用不再产生违规行为时,您可以开始强制执行可信类型:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
现在,无论您的 Web 应用有多复杂,唯一可能引入 DOM XSS 漏洞的是您的某项政策中的代码,您可以通过限制政策创建进一步锁定该代码。