深入理解CSRF攻击与防护机制

发布于:2025-06-28 ⋅ 阅读:(15) ⋅ 点赞:(0)

Hi,我是布兰妮甜 !在当今高度互联的Web应用环境中,安全威胁无处不在。CSRF(跨站请求伪造)作为一种常见的Web安全漏洞,长期位居OWASP Top 10安全威胁榜单。这种攻击方式利用了网站对用户浏览器的信任,可能导致用户在不知情的情况下执行非预期的操作,如资金转账、密码更改等。本文将深入剖析CSRF攻击原理,详细介绍多种有效的防护机制,并提供实用的JavaScript代码示例,帮助开发者构建更加安全的Web应用。无论您是前端还是后端开发者,理解并实施CSRF防护都是开发现代Web应用的必备技能。



一、什么是CSRF攻击?

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的Web安全威胁,攻击者诱使用户在不知情的情况下,以用户身份向服务器发送恶意请求。这种攻击利用了Web应用对用户浏览器的信任机制。

简单来说,当用户登录某个网站后,攻击者可以构造一个恶意页面,诱使用户访问,该页面会自动向目标网站发送请求,由于用户已登录,这些请求会带上合法的会话凭证,从而执行攻击者预设的操作。

二、CSRF攻击原理

  1. 用户已登录目标网站
  2. 目标网站没有足够的CSRF防护措施
  3. 用户访问了攻击者构造的恶意页面

攻击示例

假设有一个银行网站的转账接口:

POST /transfer HTTP/1.1
Host: bank.example.com
Content-Type: application/x-www-form-urlencoded

amount=1000&to=attacker

攻击者可以构造如下恶意页面:

<!-- 恶意网站上的页面 -->
<body onload="document.forms[0].submit()">
  <form action="https://bank.example.com/transfer" method="POST">
    <input type="hidden" name="amount" value="1000" />
    <input type="hidden" name="to" value="attacker" />
  </form>
</body>

当已登录银行网站的用户访问此页面时,会自动提交转账表单,完成攻击。

三、CSRF防护机制

3.1 验证HTTP Referer头部

检查请求的来源是否合法:

function checkReferer(req) {
  const referer = req.headers.referer;
  if (!referer || !referer.startsWith('https://yourdomain.com')) {
    throw new Error('Invalid request source');
  }
}

缺点:Referer可能被篡改或缺失(某些隐私设置会禁止发送Referer)。

3.2 使用CSRF Token

这是最常用的防护方法,原理是:

  1. 服务器生成一个随机token,存储在session中
  2. 将token嵌入表单或meta标签
  3. 提交表单时带上token,服务器验证token是否匹配

服务端实现示例

const express = require('express');
const session = require('express-session');
const crypto = require('crypto');

const app = express();

app.use(session({
  secret: 'your secret key',
  resave: false,
  saveUninitialized: true
}));

// 生成CSRF Token中间件
app.use((req, res, next) => {
  if (!req.session.csrfToken) {
    req.session.csrfToken = crypto.randomBytes(16).toString('hex');
  }
  res.locals.csrfToken = req.session.csrfToken;
  next();
});

// 表单页面
app.get('/form', (req, res) => {
  res.send(`
    <form action="/process" method="POST">
      <input type="hidden" name="_csrf" value="${res.locals.csrfToken}">
      <input type="text" name="data">
      <button type="submit">Submit</button>
    </form>
  `);
});

// 处理表单提交
app.post('/process', (req, res) => {
  const { _csrf, data } = req.body;
  if (_csrf !== req.session.csrfToken) {
    return res.status(403).send('Invalid CSRF token');
  }
  
  // 处理合法请求
  res.send(`Data received: ${data}`);
});

app.listen(3000);

前端AJAX请求示例

// 从meta标签获取CSRF Token
function getCSRFToken() {
  return document.querySelector('meta[name="csrf-token"]').content;
}

// 发起AJAX请求
function postData(data) {
  fetch('/api/endpoint', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': getCSRFToken()
    },
    body: JSON.stringify(data)
  })
  .then(response => response.json())
  .then(data => console.log(data));
}

3.3 SameSite Cookie属性

SameSite是Cookie的一个属性,可以限制第三方网站发起的请求携带Cookie:

// 设置SameSite属性
app.use(session({
  secret: 'your secret key',
  cookie: {
    sameSite: 'strict', // 或 'lax'
    secure: true // 仅HTTPS
  }
}));

SameSite有三种模式:

  • Strict:完全禁止第三方Cookie
  • Lax:允许部分安全请求(如导航)携带Cookie
  • None:不限制(必须与Secure一起使用)

3.4 双重提交Cookie验证

这种方法将CSRF Token同时放在Cookie和请求中(表单或头部),服务器验证两者是否匹配:

// 设置CSRF Token Cookie
app.use((req, res, next) => {
  const token = crypto.randomBytes(16).toString('hex');
  res.cookie('XSRF-TOKEN', token, { httpOnly: false });
  req.csrfToken = token;
  next();
});

// 验证中间件
app.use((req, res, next) => {
  const cookieToken = req.cookies['XSRF-TOKEN'];
  const bodyToken = req.body._csrf || req.headers['x-csrf-token'];
  
  if (!cookieToken || cookieToken !== bodyToken) {
    return res.status(403).send('CSRF validation failed');
  }
  next();
});

四、现代框架中的CSRF防护

4.1 Express.js中使用csurf中间件

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

// 表单页面
app.get('/form', csrfProtection, (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

// 处理表单提交
app.post('/process', csrfProtection, (req, res) => {
  // 如果token验证失败会自动返回403
  res.send('Form processed');
});

4.2 在React中处理CSRF Token

// 从服务器获取CSRF Token并存储在meta标签
fetch('/csrf-token')
  .then(res => res.json())
  .then(data => {
    const meta = document.createElement('meta');
    meta.name = 'csrf-token';
    meta.content = data.token;
    document.head.appendChild(meta);
  });

// 封装带有CSRF Token的fetch
function secureFetch(url, options = {}) {
  const token = document.querySelector('meta[name="csrf-token"]').content;
  options.headers = {
    ...options.headers,
    'X-CSRF-Token': token
  };
  return fetch(url, options);
}

五、总结

CSRF防护是现代Web应用安全的重要组成部分。通过理解CSRF攻击原理和掌握各种防护技术,开发者可以有效地保护用户免受此类攻击。在实际开发中,建议结合使用CSRF Token和SameSite Cookie属性,并根据应用的具体需求选择合适的防护策略。


网站公告

今日签到

点亮在社区的每一天
去签到