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 协议》,转载必须注明作者和本文链接
如果是真·前后端分离是不需要考虑 csrf 攻击的,因为没有 cookie 存在
@leo 你说会员信息cookie吗?你都存session里了怎么不会传到前端cookie里呢
@自由飞 真·前后端分离更不会有 session
@leo 我有点蒙,如果你登录时不把用户信息存入session中,然后响应cookie值,你怎么在访问以后接口中怎么判断用户是否登录?大神请指点
@自由飞 前后分离意味着后端只提供纯api接口不做其他支持 session cookies都不存在了 通过token来验证身份 路由也是由前端实现了
前后分离只有 token验证用户, 没有 session cookie, 但是一般要做跨域处理(前后端不在同一个域名下)
看来你没理解传统
MVC
开发模式和前后端分离有什么区别现在很多网站是工程上的前后端分离(本质上就是后端上了view层),只在浏览器上并且使用cookie和session~我觉得楼主的方法和思路挺好,只要api只给此网站即可(这样就很难兼容给其他网站或者app调用接口了)
api中再考虑这个就有些多此一举了.看下框架中的api路由文件和web路由文件默认自带的中间件组.
@ziyanziyu api默认中间件的确没有token检测,可是前后端分离就不会出现csrf攻击了吗?
@CorePlusPlus 你的那个token只是验证用户身份,可是csrf攻击你怎么防御?
@自由飞 https://segmentfault.com/a/1190000007932293,csrf攻击的原理.所以在纯前后端分离的项目中防止csrf攻击不是靠这个来防止的.而是在api认证token中
@自由飞 如果真前后端分离,token放local storage,前端需要自己带header上过去,没有csrf的问题啊。
其他网站在csrf攻击的时候,token带不过去的
@ziyanziyu 哈哈明白了,是我理解不到位多此一举了,本来项目就传验证用户的token了,也就不用考虑laravel框架本身的token了
前后端分离或者所谓的纯前后端分离 跟有没有cookie 有啥关系, 除非前端是native app , 前后端分离的 web 项目不需要cookie吗 csrf 就是cookie 劫持, 如果考虑到接口安全 还是要在post 提交 时 , 要么表单加 token, 要么header 加token
前后端分离,
token
生成是个问题,从后端获取,服务器连接数会翻倍,从前端生成那就没啥意义了。可以学习下攻击,然后攻击下测试看看有什么区别没有
这个有个问题,如果是前后端完全分离,接口和前端部署再不同域名下,csrf 如果还是基于session生成,就不行了