您现在的位置是:网站首页 > 前端框架(React/Vue/Angular)的 XSS 防护机制文章详情
前端框架(React/Vue/Angular)的 XSS 防护机制
陈川
【
前端安全
】
18745人已围观
8183字
XSS 攻击的基本原理
XSS(跨站脚本攻击)是一种常见的 Web 安全漏洞,攻击者通过在网页中注入恶意脚本,当其他用户访问该页面时,这些脚本会在用户的浏览器中执行。XSS 攻击可以分为三种类型:反射型、存储型和 DOM 型。
// 一个简单的反射型 XSS 示例
// 假设 URL 为:http://example.com/search?query=<script>alert('XSS')</script>
const query = new URLSearchParams(window.location.search).get('query');
document.getElementById('search-results').innerHTML = query; // 危险操作
React 的 XSS 防护机制
React 默认提供了良好的 XSS 防护,主要通过 JSX 的自动转义机制实现。React DOM 在渲染所有内容之前都会进行转义,将特殊字符转换为它们的 HTML 实体形式。
function SafeComponent() {
const userInput = '<script>alert("XSS")</script>';
return <div>{userInput}</div>; // 安全,React 会自动转义
}
dangerouslySetInnerHTML 的注意事项
虽然 React 默认安全,但使用 dangerouslySetInnerHTML
时会绕过这种保护,需要特别小心:
function DangerousComponent() {
const html = '<span style="color:red">用户内容</span>';
// 安全做法:先清理 HTML
const cleanHtml = DOMPurify.sanitize(html);
return (
<div
dangerouslySetInnerHTML={{ __html: cleanHtml }}
/>
);
}
其他安全实践
- 属性绑定安全:React 会自动处理属性绑定中的特殊字符
const userHref = "javascript:alert('XSS')";
<a href={userHref}>点击</a> // React 会阻止这种危险操作
- 使用 React 的 Context API 而非直接将数据插入脚本
// 不安全的做法
<script dangerouslySetInnerHTML={{ __html: `var config = ${JSON.stringify(userData)}` }} />
// 安全的做法
const ConfigContext = React.createContext();
<ConfigContext.Provider value={userData}>
{/* 子组件 */}
</ConfigContext.Provider>
Vue 的 XSS 防护机制
Vue 也提供了自动的 HTML 内容转义,使用双大括号语法 {{ }}
时会自动转义内容。
<template>
<div>{{ userInput }}</div> <!-- 自动转义 -->
</template>
<script>
export default {
data() {
return {
userInput: '<script>alert("XSS")</script>'
}
}
}
</script>
v-html 指令的风险
类似于 React 的 dangerouslySetInnerHTML
,Vue 的 v-html
也会绕过保护:
<template>
<div v-html="sanitizedHtml"></div>
</template>
<script>
import DOMPurify from 'dompurify';
export default {
data() {
return {
rawHtml: '<span onclick="alert(\'XSS\')">点击我</span>'
}
},
computed: {
sanitizedHtml() {
return DOMPurify.sanitize(this.rawHtml);
}
}
}
</script>
Vue 的其他安全特性
- 属性绑定安全:Vue 会自动处理动态属性中的潜在危险
<template>
<a :href="userHref">链接</a>
</template>
<script>
export default {
data() {
return {
userHref: "javascript:alert('XSS')"
}
}
}
</script>
<!-- Vue 会安全地处理这个 href -->
- 自定义指令的安全使用
<template>
<div v-safe-html="rawHtml"></div>
</template>
<script>
import DOMPurify from 'dompurify';
export default {
directives: {
safeHtml: {
inserted(el, binding) {
el.innerHTML = DOMPurify.sanitize(binding.value);
},
update(el, binding) {
el.innerHTML = DOMPurify.sanitize(binding.value);
}
}
},
data() {
return {
rawHtml: '<img src=x onerror=alert(1)>'
}
}
}
</script>
Angular 的 XSS 防护机制
Angular 的模板系统默认会对所有绑定进行净化,根据上下文采用不同的净化策略。
@Component({
template: `
<div>{{ userInput }}</div> <!-- 自动转义 -->
`
})
export class SafeComponent {
userInput = '<script>alert("XSS")</script>';
}
bypassSecurityTrust API
当确实需要插入 HTML 时,可以使用 Angular 的 DomSanitizer 服务:
import { DomSanitizer } from '@angular/platform-browser';
@Component({
template: `
<div [innerHTML]="safeHtml"></div>
`
})
export class UnsafeComponent {
constructor(private sanitizer: DomSanitizer) {}
rawHtml = '<span style="color:red">用户内容</span>';
safeHtml = this.sanitizer.bypassSecurityTrustHtml(this.rawHtml);
}
Angular 的安全上下文
Angular 会根据绑定目标自动选择正确的净化方式:
- HTML 内容:使用
innerHTML
绑定时会净化 HTML - 样式绑定:净化 CSS 内容
- URL 绑定:净化 URL 以防止 JavaScript URL
- 资源 URL:净化媒体资源 URL
@Component({
template: `
<a [href]="userUrl">链接</a>
<img [src]="userImage">
<div [style.background]="userStyle"></div>
`
})
export class VariousBindingsComponent {
userUrl = 'javascript:alert("XSS")'; // 会被净化
userImage = 'data:image/png;base64,...'; // 安全
userStyle = 'url("javascript:alert(1)")'; // 会被净化
}
框架无关的 XSS 防护策略
内容安全策略 (CSP)
CSP 是防御 XSS 的强大工具,通过 HTTP 头设置:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'
输入验证和输出编码
无论使用哪个框架,都应该:
- 验证所有用户输入
- 根据输出上下文进行适当的编码
// HTML 编码函数
function htmlEncode(str) {
return str.replace(/[&<>'"]/g,
tag => ({
'&': '&',
'<': '<',
'>': '>',
"'": ''',
'"': '"'
}[tag]));
}
使用专门的库处理危险内容
推荐使用 DOMPurify 等库处理 HTML:
import DOMPurify from 'dompurify';
const dirty = '<img src=x onerror=alert(1)>';
const clean = DOMPurify.sanitize(dirty);
安全的 Cookie 设置
// 设置 HttpOnly 和 Secure 的 Cookie
document.cookie = `sessionId=${sessionId}; Path=/; HttpOnly; Secure; SameSite=Strict`;
现代框架中的 XSS 新挑战
服务端渲染 (SSR) 的安全考虑
在 SSR 场景下,需要特别注意:
// Next.js 示例 - 安全地序列化数据
export async function getServerSideProps(context) {
const userData = fetchUserData();
return {
props: {
// 使用安全的序列化方法
userData: JSON.parse(JSON.stringify(userData))
}
};
}
Web Components 的安全使用
使用 Shadow DOM 时仍需注意:
class SafeElement extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
// 仍然需要清理内容
shadow.innerHTML = DOMPurify.sanitize(this.getAttribute('content'));
}
}
customElements.define('safe-element', SafeElement);
第三方库的安全集成
集成富文本编辑器等第三方库时:
// 使用受信任的库并配置安全选项
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
ClassicEditor
.create(document.querySelector('#editor'), {
// 配置允许的 HTML 标签和属性
htmlSupport: {
allow: [
{
name: /.*/,
attributes: true,
classes: true,
styles: true
}
]
}
})
.then(editor => {
console.log('Editor was initialized', editor);
})
.catch(error => {
console.error(error);
});
常见 XSS 漏洞场景分析
JSON 注入风险
不安全的 JSON 处理方式:
// 不安全的做法
const userData = { name: "</script><script>alert('XSS')</script>" };
const jsonStr = JSON.stringify(userData);
document.write(`<script>var data = ${jsonStr}</script>`);
// 安全的做法
const jsonStr = JSON.stringify(userData)
.replace(/</g, '\\u003c'); // 转义 < 字符
动态模板字符串的风险
// 不安全的模板字符串使用
const template = (input) => `<div>${input}</div>`;
document.body.innerHTML = template(userInput);
// 安全的替代方案
const template = (input) => {
const div = document.createElement('div');
div.textContent = input;
return div;
};
document.body.appendChild(template(userInput));
URL 处理的安全问题
// 不安全的 URL 构造
const search = 'javascript:alert(1)';
location.href = search;
// 安全的 URL 验证
function safeRedirect(url) {
const allowedProtocols = ['http:', 'https:', 'mailto:', 'tel:'];
const parsed = new URL(url, window.location.href);
if (!allowedProtocols.includes(parsed.protocol)) {
throw new Error('不安全的跳转协议');
}
return parsed.toString();
}
location.href = safeRedirect(userInput);
安全开发工作流程
代码审查中的 XSS 检查点
- 查找所有直接操作 DOM 的地方
- 检查所有动态生成的 HTML
- 审查所有第三方库的使用方式
- 验证所有 URL 和资源加载
自动化安全测试
使用工具进行 XSS 检测:
// 使用 ESLint 插件检测潜在 XSS
module.exports = {
rules: {
'no-dangerous-html': {
create(context) {
return {
JSXAttribute(node) {
if (node.name.name === 'dangerouslySetInnerHTML') {
context.report({
node,
message: '避免使用 dangerouslySetInnerHTML'
});
}
}
};
}
}
}
};
安全响应头配置
示例的安全响应头设置:
# Nginx 配置示例
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "SAMEORIGIN";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' cdn.example.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self' data:; font-src 'self' fonts.gstatic.com";
上一篇: 使用 CSP(内容安全策略)防范 XSS
下一篇: 自动化检测与工具推荐