当前位置:首页 > 学习笔记 > 正文内容

Web 安全 XSS 与 CSRF 攻击防护实战教程

Web 安全是每个开发者的必修课。XSS(跨站脚本攻击)和 CSRF(跨站请求伪造)是最常见的两类 Web 攻击,本文将从原理到实战,详细讲解这两种攻击方式及其防护策略,帮助你构建更安全的 Web 应用。

一、XSS 攻击原理与类型

XSS(Cross-Site Scripting,跨站脚本攻击)是一种代码注入攻击,攻击者通过在网页中注入恶意脚本,当其他用户浏览网页时,脚本会在用户浏览器中执行,从而窃取用户信息或执行恶意操作。

1. 反射型 XSS

反射型 XSS 是最常见的 XSS 类型。攻击者将恶意脚本作为参数嵌入 URL 中,当用户点击该链接时,服务器将参数值直接返回并渲染到页面上,导致脚本执行。

攻击场景示例:

假设有一个搜索页面,URL 如下:

// 搜索结果页面代码
const searchQuery = new URLSearchParams(window.location.search).get('q');
document.getElementById('result').innerHTML = `您搜索的是:${searchQuery}`;

// 攻击者构造的恶意 URL:
// https://example.com/search?q=
// 用户点击后,恶意脚本立即执行

这种攻击通常通过诱导用户点击恶意链接实施,如钓鱼邮件、社交工程等。

2. 存储型 XSS

存储型 XSS 是最危险的 XSS 类型。攻击者将恶意脚本持久化存储在目标服务器(如数据库、文件系统),当用户浏览包含该恶意脚本的页面时,脚本被执行。

攻击场景示例:

// 用户发表评论,未经过滤直接存储
const comment = req.body.comment;
db.query('INSERT INTO comments SET ?', { content: comment });

// 攻击者提交的评论内容:
// 

// 其他用户查看评论页面时
comments.forEach(c => {
  element.innerHTML += `
${c.content}
`; // 恶意脚本执行 });

这种攻击影响范围广、持续时间长,常见于论坛评论、用户资料、消息系统等功能。

3. DOM 型 XSS

DOM 型 XSS 完全发生在客户端,不经过服务器。攻击者通过修改页面的 DOM 环境来执行恶意脚本。

攻击场景示例:

// 使用 location.hash 直接操作 DOM
const content = location.hash.substring(1);
document.write(decodeURIComponent(content));

// 攻击 URL:
// https://example.com#

// 或者使用 eval 处理用户输入
const callback = location.search.match(/callback=([^&]*)/);
if (callback) {
  eval(callback[1]); // 极度危险!
}

DOM 型 XSS 的特点是不依赖服务器返回,完全由前端 JavaScript 逻辑缺陷导致。

二、XSS 防护策略

1. 输入输出编码

对所有用户输入进行 HTML 实体编码,将特殊字符转换为 HTML 实体:

// HTML 编码函数
function escapeHtml(text) {
  const map = {
    '&': '&',
    '<': '<',
    '>': '>',
    '"': '"',
    "'": '''
  };
  return text.replace(/[&<>"']/g, m => map[m]);
}

// 使用示例
const userInput = '';
const safeOutput = escapeHtml(userInput);
// 结果: <script>alert("XSS")</script>

2. 使用安全的 API

避免使用 innerHTMLouterHTMLdocument.write() 等危险 API,改用安全的替代方案:

// ❌ 危险写法
element.innerHTML = userInput;

// ✅ 安全写法
element.textContent = userInput;  // 自动编码
element.innerText = userInput;

// 如果必须插入 HTML,使用 DOM API
const div = document.createElement('div');
div.textContent = userInput;
container.appendChild(div);

3. 内容安全策略(CSP)

CSP 是一个强大的安全层,通过 HTTP 响应头限制资源加载来源:

// Node.js 设置 CSP 头
app.use((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'self'; " +
    "script-src 'self' 'unsafe-inline' https://trusted.cdn.com; " +
    "style-src 'self' 'unsafe-inline'; " +
    "img-src 'self' data: https:; " +
    "connect-src 'self' https://api.example.com"
  );
  next();
});

// HTML meta 标签方式
// 

4. HttpOnly Cookie

设置 Cookie 的 HttpOnly 属性,防止 JavaScript 读取敏感 Cookie:

// Node.js Express 设置 HttpOnly Cookie
res.cookie('sessionId', token, {
  httpOnly: true,    // JS 无法读取
  secure: true,      // 仅 HTTPS 传输
  sameSite: 'strict', // 防止 CSRF
  maxAge: 3600000
});

三、CSRF 攻击原理

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种利用用户已认证状态发起恶意请求的攻击。攻击者诱导已登录用户访问恶意页面,该页面自动向目标网站发送请求,由于用户已认证,请求会被服务器接受。

攻击流程

  1. 用户登录受信任网站 A,获得认证 Cookie
  2. 用户未登出网站 A,同时访问攻击者网站 B
  3. 网站 B 包含向网站 A 发送请求的代码
  4. 浏览器自动携带网站 A 的 Cookie,请求成功执行

攻击场景示例:






用户访问攻击者页面后,转账请求会自动发送,浏览器携带银行网站的认证 Cookie,银行服务器无法区分这是用户主动操作还是被伪造的请求。

四、CSRF 防护方案

1. CSRF Token

为每个会话生成唯一的 CSRF Token,嵌入表单或请求头中:

// 生成 CSRF Token
const crypto = require('crypto');

function generateCsrfToken() {
  return crypto.randomBytes(32).toString('hex');
}

// 服务器端设置
app.get('/form', (req, res) => {
  const csrfToken = generateCsrfToken();
  req.session.csrfToken = csrfToken;
  res.render('form', { csrfToken });
});

// 表单中嵌入 Token
/*
...
*/ // 验证 Token app.post('/submit', (req, res) => { const { _csrf } = req.body; if (_csrf !== req.session.csrfToken) { return res.status(403).send('CSRF 验证失败'); } // 处理请求 });

2. SameSite Cookie

设置 Cookie 的 SameSite 属性,限制跨站请求携带 Cookie:

// SameSite 属性说明
res.cookie('sessionId', token, {
  sameSite: 'strict'  // 完全禁止跨站发送
  // sameSite: 'lax'   // 允许安全的跨站导航请求
  // sameSite: 'none'   // 允许跨站发送(需配合 secure)
});

// 完整配置示例
res.cookie('auth', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  domain: '.example.com',
  path: '/',
  maxAge: 86400000
});

3. 验证 Referer 头

检查请求来源是否合法:

// 中间件验证 Referer
function checkReferer(req, res, next) {
  const referer = req.get('Referer');
  const allowedOrigins = ['https://example.com', 'https://www.example.com'];
  
  if (!referer) {
    return res.status(403).send('缺少 Referer 头');
  }
  
  const refererUrl = new URL(referer);
  if (!allowedOrigins.includes(refererUrl.origin)) {
    return res.status(403).send('非法来源');
  }
  
  next();
}

app.post('/sensitive-action', checkReferer, handler);

4. 双重 Cookie 验证

将 Token 同时放在 Cookie 和请求参数中,验证两者是否一致:

// 设置双重 Cookie
app.get('/api/data', (req, res) => {
  const csrfToken = generateCsrfToken();
  res.cookie('csrfToken', csrfToken, { httpOnly: false }); // 前端可读
  res.json({ csrfToken });
});

// 前端请求时携带 Token
fetch('/api/action', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': getCookie('csrfToken')
  },
  body: JSON.stringify(data)
});

// 后端验证
app.post('/api/action', (req, res) => {
  const cookieToken = req.cookies.csrfToken;
  const headerToken = req.headers['x-csrf-token'];
  
  if (!cookieToken || cookieToken !== headerToken) {
    return res.status(403).send('CSRF 验证失败');
  }
  // 处理请求
});

五、安全最佳实践

1. 纵深防御原则

不要依赖单一防护措施,应组合多种防护策略:

// 综合防护配置
const securityMiddleware = [
  // 1. CSP 策略
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"]
    }
  }),
  
  // 2. CSRF 防护
  csrf({ cookie: { httpOnly: true, secure: true, sameSite: 'strict' } }),
  
  // 3. XSS 过滤头
  helmet.xssFilter(),
  
  // 4. 禁止 MIME 类型嗅探
  helmet.noSniff(),
  
  // 5. 引用策略
  helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' })
];

2. 安全编码规范

  • 永不信任用户输入:所有输入都应被视为恶意数据
  • 最小权限原则:只授予必要的权限
  • 安全默认:默认配置应为最安全选项
  • 失败安全:验证失败时应拒绝而非放行

3. 安全测试清单

// 自动化安全测试示例
const securityTests = {
  // XSS 测试用例
  xssPayloads: [
    '',
    '',
    '">',
    "javascript:alert('XSS')",
    ''
  ],
  
  // CSRF 测试
  testCsrfProtection: async (endpoint) => {
    const response = await fetch(endpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ test: 'data' })
    });
    return response.status === 403; // 应被拒绝
  },
  
  // Cookie 安全检查
  checkCookieSecurity: (cookieString) => {
    const flags = ['HttpOnly', 'Secure', 'SameSite'];
    return flags.every(flag => cookieString.includes(flag));
  }
};

4. 监控与应急响应

建立安全事件监控机制,及时发现问题:

// 安全日志记录
const securityLogger = {
  logSuspiciousActivity: (req, type, details) => {
    console.warn({
      timestamp: new Date().toISOString(),
      type: type, // 'XSS_ATTEMPT', 'CSRF_FAILURE', etc.
      ip: req.ip,
      userAgent: req.get('User-Agent'),
      path: req.path,
      details: details
    });
  },
  
  // 集成告警系统
  alertTeam: (severity, message) => {
    if (severity === 'HIGH') {
      // 发送邮件/短信告警
      sendAlert(message);
    }
  }
};

六、总结

XSS 和 CSRF 是 Web 安全领域的两大经典威胁,理解它们的攻击原理是有效防护的基础。XSS 攻击通过注入恶意脚本窃取用户数据,防护核心在于输入编码、输出转义、内容安全策略;CSRF 攻击利用用户认证状态伪造请求,防护核心在于CSRF Token、SameSite Cookie、来源验证

在实际开发中,应当:

  1. 建立安全意识,将安全融入开发全流程
  2. 采用纵深防御策略,多重防护叠加
  3. 定期进行安全审计和渗透测试
  4. 保持框架和依赖库及时更新
  5. 建立安全应急响应机制

安全是一个持续的过程,而非一次性的任务。只有不断学习新的攻击手段和防护技术,才能构建真正安全的 Web 应用。

记住:永远不要信任来自用户的任何数据!

本文链接:https://www.kkkliao.cn/?id=952 转载需授权!

分享到:

版权声明:本文由廖万里的博客发布,如需转载请注明出处。


“Web 安全 XSS 与 CSRF 攻击防护实战教程” 的相关文章

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。