如何在Node+React项目中防止CSRF攻击

3,074 阅读3分钟

什么是CSRF攻击

CSRF(Corss-site request forgery)即跨站请求伪造攻击,也常缩写为XSRF,是一种常见的web攻击方式。

简单来说,CSRF攻击就是:用户访问正常网站A的时候,会在浏览器中留下一些登录信息(比如cookie),恶意网站B利用这些登录信息,伪造一些请求信息,告诉正常网站A我使合法用户,从而对网站A进行攻击。

下面通过一张图来阐述该攻击的过程和原理:

如何防御CSRF攻击

在项目中防御CSRF攻击的时候,可以遵循以下几个方面:

  • Get请求不对网站中的数据进行修改
  • 设置cookie的安全数据,不让第三方网站访问到用户的cookie
  • 组织第三方网站请求接口
  • 服务端接收到请求时,校验验证信息,比如验证码或者token

下面我们已Node + React项目为例,来阐述如何通过校验token来方式CSRF攻击。这里我们使用到一个很成熟的第三方包:csurf

需要说明的是,这个示例中后端使用cookie来辅助管理token信息,所以项目中已经使用了 cookie-parser ,更详细的信息可以参考 csurf 官当文档

从开发角度来说,可以分为三个步骤:

  1. 后端API生成token
  2. 前端发送请求,获取token并存储到本地,在后续的请求中讲token附带到请求头中
  3. 后端接收到请求,校验请求头中的token是否合法,如果合法则继续执行,否则直接返回相关报错信息给前端。

在后端API中生成token并返回给前端请求

首先执行一下脚本,讲csurf包安装到项目中:

npm install csurf

然后在项目中引入该包,写一个生成token的API,代码示例如下:

var csrf = require('csurf')

var csrfProtection = csrf({ cookie: true });
app.get(`/getCsrfToken`, csrfProtection, (req, res) => {
    res.send({ csrfToken: req.csrfToken() });
})

前端获取token并添加到后续请求头中

在使用React开发的前端项目中,可以在根组件挂载完成之后,在其他所有请求之前,调用 /getCsrfToken 请求,获取token。这里建议在根组件的 componentDidMount 生命周期中发起请求,然后将得到的token保存到sessionStorage中。示例代码如下:

import axios from 'axios';

componentDidMount() {
    axios.get('/getCsrfToken')
        .then(res => {
            if (res && res.data && res.data.csrfToken) {
                window.sessionStorage.setItem('CSRF-Token', res.data.csrfToken);
            } else {
                throw new Error("Get CSRF token failed");
            }
        }).catch(() => {
            throw new Error("Get CSRF token failed");
        });
}

获取token之后,在其他的API请求中,都将该token设置到请求头中,一起发送到后端API中。这里以 axios 为例,展示如何设置请求头:

import axios from 'axios';

// 在创建axios实例的时候,设置请求头信息
this.axios = axios.create({
    headers: {
        'CSRF-Token': window.sessionStorage.getItem('CSRF-Token') || ""
    }
})

// 修改已创建的axios实例的请求头信息
this.axios.defaults.headers.common['CSRF-Token'] =  window.sessionStorage.getItem('CSRF-Token');

如此一来,前端在发送请求的时候,就会将token信息附在请求头中,一起发给后端进行处理。

在后端校验token信息的合法性

在前面生成token信息的时候,我们已经通过以下代码创建了一个csurf实例:

var csrfProtection = csrf({ cookie: true });

我们可以直接以中间件的形式使用该实例对token进行校验,即:

app.use(csrfProtection);

到这里,一个基本的防御CSRF攻击的措施就完成了,需要注意的是,使用csurf包进行防御CSRF攻击的时候,会校验两个变量:一个是自动添加到cookie中的_csrf,一个就是上述生成的token,二者缺一不可。

写在后面

博客中如有错误之处,还希望各位达大佬予以纠正。