大家好,各位观众老爷们,今儿咱们来聊聊CSP和Trusted Types这对儿CP,看看它们是怎么联手把XSS这只烦人的小强拍死的。
先给大家倒杯茶,润润嗓子。咱们今天的内容可不少,得好好说道说道。XSS这玩意儿,大家都知道,搞不好就把你辛辛苦苦攒的家底儿给偷走了,所以防御XSS是每个前端工程师的必修课。
一、XSS,你这个磨人的小妖精!
XSS (Cross-Site Scripting) ,跨站脚本攻击,简单来说就是攻击者往你的网站里塞了一些恶意脚本,这些脚本在用户的浏览器里执行,然后攻击者就可以偷用户的信息,或者冒充用户干坏事。
XSS分三种:
- 存储型 XSS (Stored XSS): 攻击者把恶意脚本存到你的数据库里,比如评论区,然后每个访问这个评论的用户都会被攻击。这种危害最大。
- 反射型 XSS (Reflected XSS): 攻击者通过URL参数,或者POST请求,把恶意脚本传到服务器,服务器没做处理直接返回给用户,然后用户的浏览器就执行了这个恶意脚本。
- DOM 型 XSS (DOM-based XSS): 攻击者通过修改页面的DOM结构来注入恶意脚本。这种攻击不需要经过服务器,完全在客户端发生。
二、Content Security Policy (CSP):给你的网站加一道安全锁
CSP,内容安全策略,就像给你的网站加了一道安全锁,告诉浏览器哪些来源的内容是可信的,哪些是不可信的。浏览器收到CSP指令后,就会严格按照这个指令执行,拒绝加载不可信来源的内容。
CSP 的原理:
CSP 通过 HTTP 响应头 Content-Security-Policy
或者 HTML 中的 <meta>
标签来设置。它定义了一系列的指令,每个指令指定了某种类型资源的来源白名单。
CSP 的语法:
Content-Security-Policy: <指令> <来源>; <指令> <来源>; ...
- 指令 (Directive): 指定要控制哪种类型的资源,比如
script-src
控制脚本,style-src
控制样式,img-src
控制图片等等。 - 来源 (Source): 指定允许加载资源的域名、协议或者特殊值,比如
'self'
表示同源,'unsafe-inline'
允许内联脚本,'unsafe-eval'
允许使用eval()
函数等等。
CSP 的例子:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' https://cdn.example.com; img-src 'self' data:;
这个CSP策略表示:
default-src 'self'
: 默认只允许加载同源的资源。script-src 'self' https://cdn.example.com
: 只允许加载同源的脚本,以及来自https://cdn.example.com
的脚本。style-src 'self' https://cdn.example.com
: 只允许加载同源的样式,以及来自https://cdn.example.com
的样式。img-src 'self' data:
: 只允许加载同源的图片,以及使用 data URI 的图片。
CSP 的指令详解:
指令 | 描述 |
---|---|
default-src |
定义了所有其他未明确声明指令的默认策略。 |
script-src |
定义了允许加载脚本的来源。 |
style-src |
定义了允许加载样式的来源。 |
img-src |
定义了允许加载图片的来源。 |
connect-src |
定义了允许建立网络连接的来源 (例如,使用 XMLHttpRequest , WebSocket , EventSource )。 |
font-src |
定义了允许加载字体的来源。 |
media-src |
定义了允许加载媒体文件的来源 (例如,使用 <audio> , <video> , <source> )。 |
object-src |
定义了允许加载插件的来源 (例如,使用 <object> , <embed> , <applet> )。 |
frame-src |
定义了允许加载 frame 的来源 (例如,使用 <iframe> , <frame> )。 |
sandbox |
为请求的资源启用沙盒机制。 |
report-uri |
指定一个 URI,浏览器会将违反 CSP 策略的报告发送到该 URI。 |
report-to |
指定一个或多个组名称,这些组在 Report-To HTTP 响应头中定义,浏览器会将违反 CSP 策略的报告发送到这些组。这是 report-uri 的替代方案,更现代且功能更强大。 |
worker-src |
定义了允许作为 worker, shared worker 或 service worker 加载的 URL。 |
base-uri |
指定文档可以使用哪些 URL 来解析基 URL。 |
form-action |
指定可以提交表单的 URL。 |
frame-ancestors |
指定允许嵌入当前资源的来源。这与 X-Frame-Options HTTP 响应头类似,但功能更强大。 |
upgrade-insecure-requests |
指示浏览器自动将页面上的所有不安全 URL (HTTP) 升级为安全 URL (HTTPS)。这有助于防止混合内容问题。 |
CSP 的部署方式:
-
HTTP 响应头: 这是推荐的方式,因为可以灵活地控制每个页面的 CSP 策略。
HTTP/1.1 200 OK Content-Security-Policy: <CSP策略>
-
HTML
<meta>
标签: 可以在 HTML 文档的<head>
标签中使用<meta>
标签来设置 CSP 策略。<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Security-Policy" content="<CSP策略>"> </head> <body> ... </body> </html>
CSP 的两种模式:
- Enforce 模式: 这是默认模式,浏览器会严格按照 CSP 策略执行,阻止违反策略的资源加载和脚本执行。
-
Report-Only 模式: 浏览器不会阻止违反策略的资源加载和脚本执行,但是会将违反策略的报告发送到
report-uri
或者report-to
指令指定的 URI。 这种模式可以用来测试 CSP 策略,在不影响用户体验的情况下,发现潜在的安全问题。Content-Security-Policy-Report-Only: <CSP策略>
三、Strict-CSP:更严格的安全守护
Strict-CSP
是指一个配置非常严格的 CSP 策略,它旨在最大限度地减少 XSS 攻击的风险。 通常,这意味着要避免使用像 'unsafe-inline'
和 'unsafe-eval'
这样的不安全来源,并尽可能使用 nonce
或 hash
来允许特定的内联脚本和样式。
为什么需要 Strict-CSP?
普通的 CSP 策略可能会因为配置不当而留下安全漏洞。 例如,如果使用了 'unsafe-inline'
,那么攻击者仍然可以注入内联脚本来执行恶意代码。 Strict-CSP
通过禁用这些不安全的特性,来提高网站的安全性。
Strict-CSP 的关键特性:
-
禁用
unsafe-inline
和unsafe-eval
: 这是Strict-CSP
的核心原则。 这两个来源允许执行内联脚本和使用eval()
函数,这是 XSS 攻击的主要入口。 -
使用
nonce
或hash
来允许特定的内联脚本和样式: 如果必须使用内联脚本或样式,可以使用nonce
或hash
来指定允许执行的特定代码块。-
Nonce (Number used once): 一个随机字符串,在每次页面加载时生成,并添加到 CSP 策略和允许执行的内联脚本或样式的
<script>
或<style>
标签中。<script nonce="rAnd0mNumb3r"> // Your inline script </script>
Content-Security-Policy: script-src 'nonce-rAnd0mNumb3r';
-
Hash: 内联脚本或样式的 SHA256, SHA384 或 SHA512 哈希值。
<script> // Your inline script console.log('Hello, world!'); </script>
Content-Security-Policy: script-src 'sha256-yourScriptHash';
-
-
限制
default-src
: 尽可能将default-src
设置为'none'
,然后根据需要显式地允许特定类型的资源。 -
使用
upgrade-insecure-requests
: 指示浏览器自动将页面上的所有不安全 URL (HTTP) 升级为安全 URL (HTTPS)。
一个 Strict-CSP 的例子:
Content-Security-Policy:
default-src 'none';
script-src 'self' 'nonce-rAnd0mNumb3r';
style-src 'self' 'nonce-rAnd0mNumb3r';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
upgrade-insecure-requests;
部署 Strict-CSP 的步骤:
- 分析你的网站: 确定你的网站需要哪些资源,以及这些资源的来源。
- 生成
nonce
: 在服务器端生成一个随机的nonce
值,并将其添加到 CSP 策略和允许执行的内联脚本和样式中。 - 配置你的服务器: 将 CSP 策略添加到 HTTP 响应头中。
- 测试你的 CSP 策略: 使用
Report-Only
模式来测试你的 CSP 策略,确保它不会阻止正常的网站功能。 - 切换到 Enforce 模式: 一旦你确定你的 CSP 策略是正确的,就可以切换到
Enforce
模式。
代码示例(Python Flask):
from flask import Flask, render_template, make_response
import secrets
app = Flask(__name__)
@app.route('/')
def index():
nonce = secrets.token_urlsafe(16)
csp = f"""
default-src 'none';
script-src 'self' 'nonce-{nonce}';
style-src 'self' 'nonce-{nonce}';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
upgrade-insecure-requests;
"""
resp = make_response(render_template('index.html', nonce=nonce))
resp.headers['Content-Security-Policy'] = csp
return resp
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html>
<head>
<title>Strict CSP Example</title>
</head>
<body>
<h1>Hello, world!</h1>
<script nonce="{{ nonce }}">
console.log('This is an inline script!');
</script>
<style nonce="{{ nonce }}">
body {
background-color: #f0f0f0;
}
</style>
</body>
</html>
四、Trusted Types:让你的DOM操作更安全
Trusted Types 是一种新的 Web API,旨在防止 DOM 型 XSS 攻击。 它通过要求所有将数据插入 DOM 的操作都必须使用经过 类型转换 的值来实现这一点。 换句话说,你不能直接将字符串插入 DOM,而是需要先将它转换为 TrustedHTML
, TrustedScript
或 TrustedScriptURL
类型。
Trusted Types 的原理:
Trusted Types 通过引入一个新的类型系统来限制 DOM 操作。 它定义了三种新的类型:
TrustedHTML
: 表示安全的 HTML 片段。TrustedScript
: 表示安全的 JavaScript 代码。TrustedScriptURL
: 表示安全的 JavaScript URL。
要将字符串插入 DOM,你需要先使用一个 策略 来将它转换为这些类型之一。 策略是一个 JavaScript 函数,它接收一个字符串,并返回一个 TrustedHTML
, TrustedScript
或 TrustedScriptURL
对象。 策略可以对字符串进行验证和清理,以确保它不包含恶意代码。
Trusted Types 的例子:
// 创建一个策略
const policy = trustedTypes.createPolicy('myPolicy', {
createHTML: (input) => {
// 对输入进行验证和清理
const cleanInput = DOMPurify.sanitize(input); // 使用 DOMPurify 进行 HTML 清理
return cleanInput; // 假设 DOMPurify 返回一个 TrustedHTML 对象
},
createScriptURL: (input) => {
// 验证输入是否是一个安全的 URL
if (input.startsWith('https://example.com/')) {
return input; // 假设返回一个 TrustedScriptURL 对象
}
throw new Error('Invalid script URL');
},
createScript: (input) => {
// 验证输入是否是一个安全的 JavaScript 代码
if (input.indexOf('alert(') === -1) {
return input; // 假设返回一个 TrustedScript 对象
}
throw new Error('Invalid script');
}
});
// 使用策略来创建 TrustedHTML 对象
const trustedHTML = policy.createHTML('<p>Hello, world!</p><img src=x onerror=alert(1)>');
// 将 TrustedHTML 对象插入 DOM
document.getElementById('container').innerHTML = trustedHTML;
// 使用策略来创建 TrustedScriptURL 对象
const trustedScriptURL = policy.createScriptURL('https://example.com/script.js');
// 将 TrustedScriptURL 对象插入 DOM
const script = document.createElement('script');
script.src = trustedScriptURL;
document.body.appendChild(script);
Trusted Types 的步骤:
-
启用 Trusted Types: 通过设置 CSP 策略来启用 Trusted Types。
Content-Security-Policy: require-trusted-types-for 'script';
-
创建策略: 使用
trustedTypes.createPolicy()
函数来创建策略。 -
使用策略来创建 Trusted Types 对象: 使用策略的
createHTML()
,createScriptURL()
或createScript()
方法来将字符串转换为TrustedHTML
,TrustedScript
或TrustedScriptURL
对象。 -
将 Trusted Types 对象插入 DOM: 使用
innerHTML
,src
等属性来将 Trusted Types 对象插入 DOM。
Trusted Types 的兼容性:
Trusted Types 的兼容性目前还不是很好,需要使用 polyfill 来支持旧版本的浏览器。
Trusted Types 的优势:
- 防止 DOM 型 XSS 攻击: 通过要求所有 DOM 操作都使用经过类型转换的值,可以有效地防止 DOM 型 XSS 攻击。
- 提高代码的可维护性: Trusted Types 可以帮助你更好地理解和控制你的代码中的 DOM 操作。
- 提供更好的安全保障: Trusted Types 可以提供比传统的 XSS 防御方法更好的安全保障。
五、CSP + Trusted Types:双剑合璧,天下无敌?
CSP 和 Trusted Types 可以一起使用,以提供更强大的 XSS 防御。 CSP 可以限制资源的来源,防止加载恶意脚本,而 Trusted Types 可以防止 DOM 型 XSS 攻击。
如何一起使用 CSP 和 Trusted Types:
-
启用 Trusted Types: 通过设置 CSP 策略来启用 Trusted Types。
Content-Security-Policy: require-trusted-types-for 'script';
-
配置 CSP 策略: 配置 CSP 策略,限制资源的来源,并允许加载 Trusted Types polyfill。
Content-Security-Policy: require-trusted-types-for 'script'; script-src 'self' https://polyfill.io/v3/polyfill.min.js;
-
创建策略: 使用
trustedTypes.createPolicy()
函数来创建策略。 -
使用策略来创建 Trusted Types 对象: 使用策略的
createHTML()
,createScriptURL()
或createScript()
方法来将字符串转换为TrustedHTML
,TrustedScript
或TrustedScriptURL
对象。 -
将 Trusted Types 对象插入 DOM: 使用
innerHTML
,src
等属性来将 Trusted Types 对象插入 DOM。
总结:
CSP 和 Trusted Types 都是强大的 XSS 防御工具。 CSP 可以限制资源的来源,防止加载恶意脚本,而 Trusted Types 可以防止 DOM 型 XSS 攻击。 通过一起使用 CSP 和 Trusted Types,可以提供更强大的 XSS 防御。
注意事项:
- CSP 和 Trusted Types 的配置需要仔细考虑,避免因为配置不当而留下安全漏洞。
- Trusted Types 的兼容性目前还不是很好,需要使用 polyfill 来支持旧版本的浏览器。
- XSS 防御是一个持续的过程,需要不断地学习和更新你的知识。
好了,今天的讲座就到这里。 希望大家都能学会如何使用 CSP 和 Trusted Types 来保护你的网站,远离 XSS 攻击! 下次有机会再跟大家分享更多的安全知识。 拜拜!