您现在的位置是:网站首页 > 前端框架中的 CSRF 防护文章详情

前端框架中的 CSRF 防护

CSRF 攻击的基本原理

CSRF(Cross-Site Request Forgery)跨站请求伪造是一种常见的Web安全威胁。攻击者诱导用户在已认证的Web应用中执行非预期的操作。这种攻击利用了Web应用对用户浏览器的信任机制,通过伪造请求让用户在不知情的情况下提交恶意请求。

典型的CSRF攻击流程:

  1. 用户登录可信网站A,并在本地保存了认证信息(如Cookie)
  2. 用户在不登出网站A的情况下访问恶意网站B
  3. 网站B包含针对网站A的恶意请求
  4. 用户的浏览器自动携带网站A的认证信息发送请求
  5. 网站A服务器认为这是用户的合法操作
// 恶意网站可能包含的代码
<img src="https://bank.com/transfer?to=hacker&amount=10000" width="0" height="0">

前端框架的CSRF防护机制

现代前端框架提供了多种内置机制来防范CSRF攻击,这些机制通常与后端配合使用形成完整防护。

1. 自动CSRF Token管理

主流框架如Angular、React等支持自动处理CSRF Token。以Angular为例:

// Angular HttpClient会自动处理名为XSRF-TOKEN的Cookie
// 并在后续请求中添加X-XSRF-TOKEN头

// 后端设置Cookie
Set-Cookie: XSRF-TOKEN=abc123; Path=/; Secure; HttpOnly

// 前端请求自动添加
GET /api/data HTTP/1.1
Host: example.com
X-XSRF-TOKEN: abc123

2. 双重Cookie验证

这种方案要求前端在Cookie和请求头中都携带Token:

// 前端实现示例
function setCSRFCookie() {
  const token = generateToken();
  document.cookie = `CSRF-TOKEN=${token}; Path=/; Secure`;
  return token;
}

// 发送请求时
fetch('/api/data', {
  method: 'POST',
  headers: {
    'X-CSRF-TOKEN': getCookie('CSRF-TOKEN'),
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({...})
});

3. SameSite Cookie属性

现代浏览器支持SameSite Cookie属性,可以有效防止CSRF:

// 后端设置
Set-Cookie: sessionid=xyz123; Path=/; Secure; HttpOnly; SameSite=Strict

SameSite有三种模式:

  • Strict:完全禁止第三方Cookie
  • Lax:允许部分安全请求(如导航)携带Cookie
  • None:允许所有跨站Cookie(必须同时设置Secure)

各框架具体实现方案

React中的CSRF防护

React本身不提供CSRF防护,但可以轻松集成各种方案:

// 使用axios拦截器添加CSRF Token
import axios from 'axios';

axios.interceptors.request.use(config => {
  const token = document.cookie
    .split('; ')
    .find(row => row.startsWith('XSRF-TOKEN='))
    ?.split('=')[1];
  
  if (token) {
    config.headers['X-XSRF-TOKEN'] = token;
  }
  return config;
});

// 组件中使用
function TransferForm() {
  const handleSubmit = async (e) => {
    e.preventDefault();
    await axios.post('/api/transfer', {...});
  };
  
  return (
    <form onSubmit={handleSubmit}>
      {/* 表单内容 */}
    </form>
  );
}

Vue中的CSRF防护

Vue可以与各种HTTP库配合实现防护:

// 使用vue-resource
Vue.http.interceptors.push((request, next) => {
  const token = getCookie('XSRF-TOKEN');
  if (token) {
    request.headers.set('X-XSRF-TOKEN', token);
  }
  next();
});

// 或者在Vue 3中使用axios
app.config.globalProperties.$http = axios.create({
  headers: {
    'X-Requested-With': 'XMLHttpRequest'
  }
});

Angular的CSRF防护

Angular提供了开箱即用的支持:

// app.module.ts
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule,
    HttpClientXsrfModule.withOptions({
      cookieName: 'XSRF-TOKEN',
      headerName: 'X-XSRF-TOKEN'
    })
  ]
})
export class AppModule {}

// 组件中使用
this.http.post('/api/data', payload).subscribe(...);

高级防护策略

1. 可验证的请求来源

检查Origin和Referer头部:

// 后端中间件示例(Node.js)
app.use((req, res, next) => {
  const origin = req.get('Origin');
  const referer = req.get('Referer');
  
  if (!isValidOrigin(origin) || !isValidReferer(referer)) {
    return res.status(403).send('Invalid request source');
  }
  next();
});

2. 一次性Token

每次请求使用不同的Token:

// 前端获取Token
async function getCSRFToken() {
  const response = await fetch('/api/csrf-token');
  const { token } = await response.json();
  return token;
}

// 使用Token
async function makeRequest(payload) {
  const token = await getCSRFToken();
  const response = await fetch('/api/data', {
    method: 'POST',
    headers: {
      'X-CSRF-TOKEN': token
    },
    body: JSON.stringify(payload)
  });
  return response.json();
}

3. 自定义请求头

添加非标准头部的防护:

// 前端设置
fetch('/api/data', {
  method: 'POST',
  headers: {
    'X-Custom-Header': 'value',
    'X-Requested-With': 'XMLHttpRequest'
  }
});

// 后端验证
if (!req.headers['x-requested-with']) {
  return res.status(403).send('Missing required header');
}

实际应用中的注意事项

1. Token的存储与传输

  • 避免将Token存储在localStorage中
  • 确保Token通过安全通道传输(HTTPS)
  • 为敏感操作使用短期有效的Token
// 不安全的做法
localStorage.setItem('csrfToken', token);

// 正确的做法
document.cookie = `CSRF-TOKEN=${token}; Path=/; Secure; SameSite=Strict`;

2. API设计原则

  • RESTful API应区分安全和非安全端点
  • 对状态修改操作强制使用CSRF防护
  • 为不同功能使用不同的Token
// API路由示例
router.post('/api/transfer', csrfProtection, transferController);
router.get('/api/transactions', transactionController); // 不需要CSRF

3. 与认证机制的配合

CSRF防护应与认证方案协同工作:

// JWT + CSRF组合方案
// 1. 登录成功后返回JWT和CSRF Token
{
  "token": "jwt.xyz.123",
  "csrfToken": "abc123"
}

// 2. 前端存储
localStorage.setItem('token', data.token);
document.cookie = `CSRF-TOKEN=${data.csrfToken}; Secure; HttpOnly`;

// 3. 后续请求
fetch('/api/data', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('token')}`,
    'X-CSRF-TOKEN': getCookie('CSRF-TOKEN')
  }
});

测试与验证

1. 自动化测试方案

使用测试工具验证CSRF防护:

// 使用Jest测试CSRF防护
describe('CSRF Protection', () => {
  it('should reject requests without CSRF token', async () => {
    const response = await request(app)
      .post('/api/data')
      .expect(403);
  });

  it('should accept requests with valid CSRF token', async () => {
    const csrfToken = await getCSRFToken();
    const response = await request(app)
      .post('/api/data')
      .set('X-CSRF-TOKEN', csrfToken)
      .expect(200);
  });
});

2. 手动验证步骤

  1. 检查页面是否包含CSRF Token
  2. 验证Token是否随请求发送
  3. 尝试伪造请求测试防护效果
  4. 检查SameSite Cookie设置
<!-- 检查表单中的Token -->
<form>
  <input type="hidden" name="_csrf" value="token123">
</form>

与其他安全措施的协同

1. 内容安全策略(CSP)

CSP可以限制外部资源加载,辅助CSRF防护:

<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; script-src 'self' 'unsafe-inline'">

2. CORS策略

合理的CORS配置可以防止某些CSRF变种:

// Express CORS配置
app.use(cors({
  origin: ['https://trusted.com'],
  methods: ['GET', 'POST'],
  allowedHeaders: ['X-CSRF-TOKEN']
}));

3. 速率限制

对敏感操作添加速率限制:

// 登录尝试限制
app.post('/login', 
  rateLimit({ windowMs: 15*60*1000, max: 5 }), 
  loginController
);

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步