Laravel 前后端分离 csrf 防护

首先根据文档详解 csrf 的流程机制,然后分析下怎么在前后端分离的情况下进行 csrf 防护?
1. 首先介绍 token 的生成,在 Illuminate\Session\Store.php 中 start () 方法中,然后点击方法 regenerateToken () 方法中:

public function start()
{
    $this->loadSession();
    if (! $this->has('_token')) {
        $this->regenerateToken();  
    }
    return $this->started = true;
}

    public function regenerateToken()
{
    $this->put('_token', Str::random(40));
}

可见 token 是每次加载 session 时自动存入进 session 中。
2. 然后看前台获取 token 值,首先表单中 {{csrf_token ()}} 点入进去可以看到

function csrf_field()
{
    return new HtmlString('<input type="hidden" name="_token" value="'.csrf_token().'">'); //点入进去csrf_token方法中
}
function csrf_token()
{
    $session = app('session');
    if (isset($session)) {
        return $session->token();
    }
    throw new RuntimeException('Application session store not set.');
}

可以看到前台 token 值是从 session 存储的 token 值中获取到的。
3. 然后看中间件是怎么验证前台提交的_token 值:
在 VerifyCsrfToken.php 中继承 Middleware 类,点击进入类中查看 handle 方法

public function handle($request, Closure $next)
{
    if (
        $this->isReading($request) ||    //验证请求方式,如果不在数组['HEAD', 'GET', 'OPTIONS']中 返回false;
        $this->runningUnitTests() ||      //查看程序是否在进行测试 正式运行返回false;
        $this->inExceptArray($request) ||  //查看$except(白名单,不用验证_token)数组,如果请求路径在数组中返回true; 
        $this->tokensMatch($request)   //进行token验证
    ) {
        return $this->addCookieToResponse($request, $next($request));
    }

    throw new TokenMismatchException;
}

我们点击 $this->tokensMatch () 方法中可以看到请求中的 token 和 session 中的 token 是否相等:

    protected function tokensMatch($request)
{
    $token = $this->getTokenFromRequest($request); //从请求中获取_token值

    return is_string($request->session()->token()) &&
           is_string($token) &&
           hash_equals($request->session()->token(), $token);  //session中的token和请求中的token是否相等;
}

点击进入 $this->getTokenFromRequest () 方法中

 protected function getTokenFromRequest($request)
{
    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

    if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
        $token = $this->encrypter->decrypt($header);
    }

    return $token;
}  

从中可以看到获取请求中 token 值的几种方法:
from 表单、头部值 X-CSRF-TOKEN、头部值 X-XSRF-TOKEN 这几种方试,只要明白了中间件怎么接收 token 值进行判断,然后前台就那么几种传值方式了
第一种 form 表单传值 {{csrf_field () }} 或 ajaxpost 携带值 对应 $request->input ('_token')
第二种 header 头传值:

     <meta name="csrf-token" content="{{ csrf_token() }}">
     $.ajaxSetup({   //修改jquery库中代码
     headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
      }
 });

如果你用 vue,只需将 resources/assets/js/bootstrap.js 引入你的文件中 然后 meta 标签头部值放入总模板里,每次 axios 请求都会携带 X-CSRF-TOKEN 进入中间件进行验证,下面为 js 文件:

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
     window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
 } else {
console.error('CSRF token not found: https://learnku.com/docs/laravel/csrf#csrf-x-csrf-token');
}       

至此 从 token 值生成到前端携带 token 到后台验证已经完全走完一个流程了,接下来想一下假如前后端分离情况下怎么进行 token 验证
很简单每次提交前用 api 的形式获取 token 值传入前端(csrf_token () 获取 token 值),然后前端用 header 头形式或 ajax 形式传入过来就可以了。
如果感觉有用就赞一下呗!!!

本作品采用《CC 协议》,转载必须注明作者和本文链接
自由飞
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 22
leo

如果是真・前后端分离是不需要考虑 csrf 攻击的,因为没有 cookie 存在

6年前 评论

@leo 你说会员信息 cookie 吗?你都存 session 里了怎么不会传到前端 cookie 里呢

6年前 评论
leo

@自由飞 真・前后端分离更不会有 session

6年前 评论
myhui0926 4年前
changfengshuoxue 5年前
小李世界 4年前

@leo 我有点蒙,如果你登录时不把用户信息存入 session 中,然后响应 cookie 值,你怎么在访问以后接口中怎么判断用户是否登录?大神请指点

6年前 评论

@自由飞 前后分离意味着后端只提供纯 api 接口不做其他支持 session cookies 都不存在了 通过 token 来验证身份 路由也是由前端实现了

6年前 评论

前后分离只有 token 验证用户,没有 session cookie, 但是一般要做跨域处理 (前后端不在同一个域名下)

6年前 评论

看来你没理解传统 MVC 开发模式和前后端分离有什么区别

6年前 评论

现在很多网站是工程上的前后端分离 (本质上就是后端上了 view 层),只在浏览器上并且使用 cookie 和 session~ 我觉得楼主的方法和思路挺好,只要 api 只给此网站即可(这样就很难兼容给其他网站或者 app 调用接口了)

6年前 评论
wenber

api 中再考虑这个就有些多此一举了。看下框架中的 api 路由文件和 web 路由文件默认自带的中间件组.

6年前 评论

@ziyanziyu api 默认中间件的确没有 token 检测,可是前后端分离就不会出现 csrf 攻击了吗?

6年前 评论

@CorePlusPlus 你的那个 token 只是验证用户身份,可是 csrf 攻击你怎么防御?

6年前 评论

@自由飞 如果真前后端分离,token 放 local storage,前端需要自己带 header 上过去,没有 csrf 的问题啊。
其他网站在 csrf 攻击的时候,token 带不过去的

6年前 评论

@ziyanziyu 哈哈明白了,是我理解不到位多此一举了,本来项目就传验证用户的 token 了,也就不用考虑 laravel 框架本身的 token 了

6年前 评论
wonbin

前后端分离或者所谓的纯前后端分离 跟有没有 cookie 有啥关系, 除非前端是 native app , 前后端分离的 web 项目不需要 cookie 吗 csrf 就是 cookie 劫持, 如果考虑到接口安全 还是要在 post 提交 时 , 要么表单加 token, 要么 header 加 token

6年前 评论
游离不2

前后端分离,token 生成是个问题,从后端获取,服务器连接数会翻倍,从前端生成那就没啥意义了。

6年前 评论

可以学习下攻击,然后攻击下测试看看有什么区别没有

4年前 评论

这个有个问题,如果是前后端完全分离,接口和前端部署再不同域名下,csrf 如果还是基于 session 生成,就不行了

2年前 评论