各位观众老爷们,掌声在哪里!今天咱们来聊聊一个听起来高大上,实则能让你代码更安全的“Trusted Types”。这玩意儿就像给你的应用装了个安检门,专门拦那些来路不明的字符串,防止 XSS 攻击。
开场白:为啥我们需要 Trusted Types?
想象一下,你的网页像一个热闹的集市,各种数据进进出出。有些数据是从你信任的来源来的,比如你的后端服务器。但有些数据可能藏着坏心思,比如用户输入,第三方广告等等。这些坏家伙可能会在你的网页里偷偷塞一些恶意脚本,一旦执行,你的用户数据就可能被窃取,甚至整个网站都被控制。这就是 XSS 攻击。
传统的 XSS 防御方法,比如 HTML 转义,往往不够彻底,而且容易出错。Trusted Types 就是为了从根本上解决这个问题,它强制你必须使用“可信任”的方式来处理那些可能被用来执行脚本的字符串。
Trusted Types 的核心概念:Policy 和 Sanitizer
Trusted Types 的核心就是 Policy
和 Sanitizer
。你可以把 Policy
想象成一个“信任工厂”,它负责生产各种“可信任类型”的对象,比如 TrustedHTML
、TrustedScript
、TrustedScriptURL
。Sanitizer
则是一个“清洁工”,它可以把那些不信任的字符串清理干净,变成可信任的类型。
1. Policy:信任工厂
Policy 是 Trusted Types 的核心,它定义了如何创建可信任类型对象。你需要先创建一个 Policy,然后才能使用它来创建 TrustedHTML、TrustedScript、TrustedScriptURL 等对象。
创建 Policy
if (window.trustedTypes && window.trustedTypes.createPolicy) {
// 检查浏览器是否支持 Trusted Types
const policy = window.trustedTypes.createPolicy('my-app', {
createHTML: (string) => {
// 这里对字符串进行处理,确保它是安全的 HTML
// 例如,你可以使用 DOMPurify 来清理字符串
return string;
},
createScript: (string) => {
// 这里对字符串进行处理,确保它是安全的 JavaScript 代码
// 最好不要直接返回字符串,而是使用其他方法来创建安全的脚本
throw new Error('不允许创建动态脚本');
},
createScriptURL: (string) => {
// 这里对字符串进行处理,确保它是安全的 URL
// 例如,你可以检查 URL 是否在白名单中
if (string.startsWith('https://example.com/')) {
return string;
} else {
throw new Error('不允许加载来自该域名的脚本');
}
}
});
// 现在你可以使用 policy 来创建 Trusted Types 对象了
const trustedHTML = policy.createHTML('<p>Hello, world!</p>');
const trustedScriptURL = policy.createScriptURL('https://example.com/script.js');
// 将 Trusted Types 对象赋值给相应的 DOM 属性
document.getElementById('my-div').innerHTML = trustedHTML;
const script = document.createElement('script');
script.src = trustedScriptURL;
document.head.appendChild(script);
} else {
// 浏览器不支持 Trusted Types,降级处理
console.warn('Trusted Types is not supported.');
// 可以使用传统的 XSS 防御方法,或者禁用某些功能
}
代码解释:
window.trustedTypes && window.trustedTypes.createPolicy
:首先检查浏览器是否支持 Trusted Types。如果不支持,就降级处理,可以使用传统的 XSS 防御方法,或者禁用某些功能。window.trustedTypes.createPolicy('my-app', { ... })
:创建一个名为my-app
的 Policy。Policy 的名字可以随便取,但最好能反映你的应用或者模块的名字。createHTML
、createScript
、createScriptURL
:这三个函数分别定义了如何创建 TrustedHTML、TrustedScript、TrustedScriptURL 对象。你需要在这些函数里对字符串进行处理,确保它们是安全的。throw new Error('不允许创建动态脚本');
:在createScript
函数里,我们直接抛出了一个错误。这是因为动态创建脚本非常危险,很容易被 XSS 攻击利用。除非你有非常充分的理由,否则最好不要允许创建动态脚本。policy.createHTML('<p>Hello, world!</p>')
:使用 Policy 创建一个 TrustedHTML 对象。document.getElementById('my-div').innerHTML = trustedHTML
:将 TrustedHTML 对象赋值给innerHTML
属性。注意,你不能直接将字符串赋值给innerHTML
属性,必须先将字符串转换为 TrustedHTML 对象。script.src = trustedScriptURL
:将 TrustedScriptURL 对象赋值给script.src
属性。同样,你不能直接将字符串赋值给script.src
属性,必须先将字符串转换为 TrustedScriptURL 对象。
Policy 的作用:
- 控制可信任类型的创建: Policy 可以控制哪些字符串可以被转换为可信任类型。
- 提供安全处理逻辑: Policy 可以对字符串进行安全处理,例如 HTML 转义、URL 白名单检查等。
- 集中管理信任策略: Policy 可以集中管理你的应用的信任策略,方便维护和更新。
2. Sanitizer:清洁工
Sanitizer 是一个“清洁工”,它可以把那些不信任的字符串清理干净,变成可信任的类型。Sanitizer 通常使用 DOMPurify 等库来实现。
使用 Sanitizer
import DOMPurify from 'dompurify';
if (window.trustedTypes && window.trustedTypes.createPolicy) {
const policy = window.trustedTypes.createPolicy('my-app', {
createHTML: (string) => {
// 使用 DOMPurify 清理字符串
const cleanHTML = DOMPurify.sanitize(string);
return cleanHTML;
},
createScript: (string) => {
throw new Error('不允许创建动态脚本');
},
createScriptURL: (string) => {
if (string.startsWith('https://example.com/')) {
return string;
} else {
throw new Error('不允许加载来自该域名的脚本');
}
}
});
const untrustedHTML = '<img src="x" onerror="alert(1)">';
const trustedHTML = policy.createHTML(untrustedHTML);
document.getElementById('my-div').innerHTML = trustedHTML;
} else {
console.warn('Trusted Types is not supported.');
// 使用传统的 XSS 防御方法
const untrustedHTML = '<img src="x" onerror="alert(1)">';
const cleanHTML = DOMPurify.sanitize(untrustedHTML);
document.getElementById('my-div').innerHTML = cleanHTML;
}
代码解释:
import DOMPurify from 'dompurify'
:导入 DOMPurify 库。const cleanHTML = DOMPurify.sanitize(string)
:使用 DOMPurify 清理字符串。DOMPurify 会移除字符串中的恶意代码,例如onerror
属性。const untrustedHTML = '<img src="x" onerror="alert(1)">'
:一个包含恶意代码的字符串。const trustedHTML = policy.createHTML(untrustedHTML)
:使用 Policy 将不信任的字符串转换为 TrustedHTML 对象。DOMPurify 会在 Policy 的createHTML
函数中被调用,清理字符串。
Sanitizer 的作用:
- 清理不信任的字符串: Sanitizer 可以移除字符串中的恶意代码,例如 HTML 标签、JavaScript 代码等。
- 将不信任的字符串转换为可信任类型: Sanitizer 可以将清理后的字符串转换为 TrustedHTML、TrustedScript、TrustedScriptURL 等对象。
Trusted Types 的用法
Trusted Types 主要用于以下场景:
- 设置 DOM 属性: 例如
innerHTML
、src
、href
等。 - 创建 DOM 元素: 例如
document.createElement
、document.createTextNode
等。 - 执行 JavaScript 代码: 例如
eval
、Function
等。(强烈不建议使用)
示例:设置 innerHTML
属性
<div id="my-div"></div>
<script>
if (window.trustedTypes && window.trustedTypes.createPolicy) {
const policy = window.trustedTypes.createPolicy('my-app', {
createHTML: (string) => {
return string;
}
});
const trustedHTML = policy.createHTML('<p>Hello, world!</p>');
document.getElementById('my-div').innerHTML = trustedHTML; // 正确
// document.getElementById('my-div').innerHTML = '<p>Hello, world!</p>'; // 错误,会抛出 TypeError
} else {
document.getElementById('my-div').innerHTML = '<p>Hello, world!</p>';
}
</script>
代码解释:
document.getElementById('my-div').innerHTML = trustedHTML
:将 TrustedHTML 对象赋值给innerHTML
属性。这是正确的用法。document.getElementById('my-div').innerHTML = '<p>Hello, world!</p>'
:直接将字符串赋值给innerHTML
属性。这是错误的用法,会抛出一个TypeError
异常,告诉你不能直接使用字符串。
示例:设置 src
属性
<img id="my-image">
<script>
if (window.trustedTypes && window.trustedTypes.createPolicy) {
const policy = window.trustedTypes.createPolicy('my-app', {
createScriptURL: (string) => {
if (string.startsWith('https://example.com/')) {
return string;
} else {
throw new Error('不允许加载来自该域名的脚本');
}
}
});
const trustedScriptURL = policy.createScriptURL('https://example.com/image.jpg');
document.getElementById('my-image').src = trustedScriptURL; // 正确
// document.getElementById('my-image').src = 'https://example.com/image.jpg'; // 错误,会抛出 TypeError
} else {
document.getElementById('my-image').src = 'https://example.com/image.jpg';
}
</script>
Trusted Types 的优点:
- 更强的安全性: Trusted Types 可以从根本上防止 XSS 攻击。
- 更少的漏洞: Trusted Types 可以减少因人为错误而导致的 XSS 漏洞。
- 更好的可维护性: Trusted Types 可以集中管理你的应用的信任策略,方便维护和更新。
Trusted Types 的缺点:
- 兼容性问题: Trusted Types 并不是所有浏览器都支持。
- 学习成本: Trusted Types 需要一定的学习成本。
- 代码改造成本: 将现有的代码迁移到 Trusted Types 需要一定的改造成本。
Trusted Types 的兼容性
Trusted Types 的兼容性目前还不是很好。只有 Chrome 和 Edge 等少数浏览器支持 Trusted Types。
你可以使用以下代码来检查浏览器是否支持 Trusted Types:
if (window.trustedTypes) {
console.log('Trusted Types is supported.');
} else {
console.log('Trusted Types is not supported.');
}
如果浏览器不支持 Trusted Types,你可以使用传统的 XSS 防御方法,例如 HTML 转义。
Trusted Types 的未来
Trusted Types 是一个很有前景的技术,它可以有效地防止 XSS 攻击。随着浏览器对 Trusted Types 的支持越来越好,它将会成为 Web 开发的标准配置。
总结
Trusted Types 就像给你的应用装了个安检门,专门拦那些来路不明的字符串。它通过 Policy 和 Sanitizer 来控制可信任类型的创建,并提供安全处理逻辑。虽然 Trusted Types 有一定的学习成本和代码改造成本,但它能显著提高你的应用的安全性,减少 XSS 漏洞。
一些建议
- 尽早开始使用 Trusted Types: 即使你的项目现在还不大,也可以开始尝试使用 Trusted Types。
- 逐步迁移: 不要试图一次性将所有的代码都迁移到 Trusted Types。可以先从最容易被 XSS 攻击的地方开始。
- 使用 Sanitizer: 使用 Sanitizer 可以简化你的代码,并提高安全性。
- 关注 Trusted Types 的发展: 随着 Trusted Types 的发展,会有越来越多的工具和库可以帮助你更好地使用它。
最后的唠叨
各位观众老爷们,安全无小事!希望今天的讲座能帮助大家更好地理解 Trusted Types,并在你的项目中应用它,让你的代码更安全,让你的用户更放心!
感谢大家的收听,我们下期再见!
表格总结
特性 | 描述 |
---|---|
Trusted Types | 一个 Web API,旨在防止 XSS 攻击,它强制开发者使用“可信任”的方式处理可能被用来执行脚本的字符串。 |
Policy | 一个“信任工厂”,负责生产各种“可信任类型”的对象,例如 TrustedHTML 、TrustedScript 、TrustedScriptURL 。它可以控制哪些字符串可以被转换为可信任类型,并提供安全处理逻辑。 |
Sanitizer | 一个“清洁工”,可以把那些不信任的字符串清理干净,变成可信任的类型。通常使用 DOMPurify 等库来实现。 |
优点 | 更强的安全性,更少的漏洞,更好的可维护性。 |
缺点 | 兼容性问题,学习成本,代码改造成本。 |
兼容性 | 目前只有 Chrome 和 Edge 等少数浏览器支持。 |