减小应用的 DOM XSS 攻击面。
为什么要关注短视频?
基于 DOM 的跨站脚本攻击 (DOM XSS) 是最常见的网络安全漏洞之一,很容易在应用中引入它。可信类型可将危险的 Web API 函数默认设置为安全状态,从而为您提供编写、安全审核和维护应用不存在 DOM XSS 漏洞的工具。Chrome 83 支持可信类型,并且 polyfill 适用于其他浏览器。如需了解最新的跨浏览器支持信息,请参阅浏览器兼容性。
背景
多年来,DOM XSS 一直是最普遍、最危险的 Web 安全漏洞之一。
跨站脚本攻击分为两类。有些 XSS 漏洞是由以不安全的方式创建用来构成网站的 HTML 代码的服务器端代码导致的。而其他代码则是在客户端上存在根本原因,即 JavaScript 代码使用用户控制的内容调用危险函数。
为防止服务器端 XSS,请勿通过串联字符串来生成 HTML,而应使用安全的上下文自动转义模板库。使用基于 Nonce 的内容安全政策,在不可避免地发生 bug 时采取额外的缓解措施。
现在,浏览器还可以借助可信类型来帮助防止客户端(也称为基于 DOM)的 XSS。
API 简介
可信类型的工作原理是锁定以下存在风险的接收器函数。您可能已经认识其中一些,因为出于安全考虑,浏览器供应商和 Web 框架已经让您不再使用这些功能。
- 脚本操作:
<script src>
和设置<script>
元素的文本内容。 从字符串生成 HTML:
innerHTML
、outerHTML
、insertAdjacentHTML
、<iframe> srcdoc
、document.write
、document.writeln
和DOMParser.parseFromString
执行插件内容:
<embed src>
、<object data>
和<object codebase>
运行时 JavaScript 代码编译:
eval
、setTimeout
、setInterval
、new Function()
可信类型要求您先处理数据,然后再将其传递给上述接收器函数。仅使用字符串将会失败,因为浏览器不确定数据是否可信:
anElement.innerHTML = location.href;
为了表示数据已经过安全处理,请创建一个特殊对象 -“可信类型”。
anElement.innerHTML = aTrustedHTML;
可信类型可大幅减少应用的 DOM XSS 攻击面。该工具可简化安全审核,并可让您在运行时在浏览器中对代码进行编译、lint 检查或捆绑代码时执行基于类型的安全检查。
如何使用可信类型
为接收内容安全政策违规行为报告做好准备
您可以部署报告收集器(例如开源 go-csp-collector),也可以使用某个等效的商业报告收集器。您还可以在浏览器中调试违规行为:
js
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
添加仅用于报告的 CSP 标头
将以下 HTTP 响应标头添加到要迁移到可信类型的文档中。text
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 违规问题
从现在起,每当可信类型检测到违规行为时,系统都会向配置的 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 行的 innerHTML
是使用以 <img src=x
开头的字符串调用的。这些信息应该能够帮助您缩小哪些代码部分可能会引入 DOM XSS 并需要更改。
修正违规问题
您可以通过多种方式解决“受信任类型”违规行为。您可以移除违规代码、使用库、创建“可信类型”政策,或在万不得已的情况下创建默认政策。
重写违规代码
也许您不再需要不合规的功能,或者可以在不使用容易出错的函数的情况下以现代方式重写它?
el.innerHTML = '';
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
使用库
某些库已生成可传递给接收器函数的可信类型。例如,您可以使用 DOMPurify 清理 HTML 代码段,移除 XSS 载荷。
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify 支持可信类型,并将返回封装在 TrustedHTML
对象中且已经过净化的 HTML,以确保浏览器不会生成违规行为。
创建可信类型政策
有时,无法移除该功能,并且没有库可以清理该值并为您创建可信类型。在这种情况下,请自行创建一个“可信类型”对象。
为此,请先创建一项政策。政策是可信类型的工厂,可对其输入强制执行某些安全规则:
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 漏洞的因素就是您的一项政策中的代码。您还可以通过限制政策创建来进一步锁定这一点。