Skip to content
imshengli blog
Go back

Web 安全基础:XSS 与 CSRF 攻击原理及防御

Web 安全基础:XSS 三种类型与防御(CSP/编码/HttpOnly)、CSRF 攻击原理与防护(Token/SameSite)。

· 5 min

XSS

XSS,跨站脚本攻击,即 Cross Site Scripting,为了和 CSS 不重名,所以取名为 XSS。

原理

攻击者通过畸形的输入,将恶意的 JS 代码插入到了页面中。当其他用户浏览该网页时,恶意代码会被执行,从而达到攻击的目的。

XSS 攻击发生的根本原因是对用户输入的信任

分类

反射型 XSS

用户的输入直接传到后台(不保存),后台未经过滤,直接将输入数据返回给前台。

http://example.com/search?param=<script>alert('XSS')</script>

攻击者将恶意 URL 通过邮件、聊天工具发送给受害者,诱导其点击。

存储型 XSS

最危险的一类,攻击过程:

  1. 攻击者在评论区提交恶意脚本
  2. 后台未经处理直接存储到数据库
  3. 其他用户请求该页面,恶意脚本随数据返回
  4. 用户打开页面,XSS 代码自动执行

DOM-XSS

纯粹发生在客户端的 XSS 攻击,不经过服务器。恶意代码通过修改页面 DOM 来执行。

// 漏洞代码:从 URL hash 获取内容直接写入 DOM
const hash = window.location.hash.substring(1);
document.getElementById('greeting').innerHTML = '你好, ' + hash;

// 攻击者构造:
// http://example.com/page#<img src=x onerror=alert(document.cookie)>

另一个常见场景:

// URL 参数未校验直接用于跳转
const url = new URLSearchParams(window.location.search).get('redirect');
// 攻击者构造: ?redirect=javascript:alert(document.cookie)
window.location.href = url;

防范 XSS

1. 输出编码

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

// 更安全的做法:用 textContent 代替 innerHTML
element.textContent = userInput;

JavaScript 无法读取设置了 HttpOnly 的 Cookie,即使 XSS 攻击成功也无法窃取:

// Node.js Express
res.cookie('sessionId', 'abc123', {
  httpOnly: true,
  secure: true,
  sameSite: 'Strict'
});

3. Content Security Policy (CSP)

通过白名单机制限制页面可以加载和执行的资源:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src *;

CSP 部署后的效果:


CSRF

CSRF,跨站请求伪造,即 Cross Site Request Forgery。

原理

在受害者毫不知情的情况下以受害者名义发送请求。原理是借助受害者的 Cookie 来骗取服务器的信任

攻击流程

1. 用户 T 登录银行网站 bank.com,获取 Cookie
2. 用户 T 未登出,访问了恶意网站 evil.com
3. evil.com 页面包含隐藏表单,自动向 bank.com 发起转账请求
4. 浏览器自动带上 bank.com 的 Cookie
5. bank.com 验证 Cookie 有效,执行转账
6. 用户 T 的钱被转走,全程无感知

恶意页面代码:

<form action="https://bank.com/transfer" method="POST" id="hack">
  <input type="hidden" name="to" value="attacker" />
  <input type="hidden" name="amount" value="10000" />
</form>
<script>document.getElementById('hack').submit();</script>

防范 CSRF

1. CSRF Token 验证

服务器生成随机 Token 嵌入表单,提交时验证 Token 有效性:

const crypto = require('crypto');

// 生成 Token
function generateCsrfToken(session) {
  const token = crypto.randomBytes(32).toString('hex');
  session.csrfToken = token;
  return token;
}

// 验证中间件
function verifyCsrfToken(req, res, next) {
  const token = req.body._csrf || req.headers['x-csrf-token'];
  if (token !== req.session.csrfToken) {
    return res.status(403).json({ error: 'CSRF token mismatch' });
  }
  next();
}

限制第三方网站发起请求时是否携带 Cookie:

Set-Cookie: sessionId=abc123; SameSite=Lax; Secure; HttpOnly

利用攻击者无法读取跨域 Cookie 的特点:

// 前端从 Cookie 中读取 Token 放到请求头
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': getCookie('csrf-token')
  },
  body: JSON.stringify(data)
});
// 攻击者能让浏览器发送 Cookie,但无法用 JS 读取跨域 Cookie 值

4. 检查 Referer / Origin

function checkOrigin(req, res, next) {
  const origin = req.headers.origin || req.headers.referer;
  if (!origin || !origin.startsWith('https://bank.com')) {
    return res.status(403).json({ error: 'Invalid origin' });
  }
  next();
}

综合防护清单

XSS 防护:

CSRF 防护:

通用原则:


Share this post on:

Previous Post
JSON 深入:parse/stringify 高级用法与常见陷阱
Next Post
ES6 Object:解构赋值、扩展运算符与可选链