Laravel上实现CSRF保护

作者: linice | 来源:发表于2017-05-21 21:35 被阅读193次

    环境

    Laravel5.4

    什么是CSRF?

    维基百科的解释如下:
    跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。[1] 跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

    那是谁利用了“网站对用户网页浏览器的任信”?可能是骇客,也可能是机器人,还可能是没那么友善的用户。

    因此,我们有必要做好CSRF的保护。

    在Laravel上如何实现CSRF保护?

    开启web.php路由中间件的CSRF保护

    vi app/Http/Kernel.php

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\VerifyCsrfToken::class,
        ],
    ];
    

    开启axios自动在ajax请求时发送X-CSRF-TOKEN http header

    vi resources/assets/js/bootstrap.js

    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = window.Laravel.csrfToken;
    window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
    

    在layout文件(如master.blade.php)或任何需要CSRF保护的blade模板文件设置csrf token

    • 在head处设置meta
    <meta name="csrf-token" content="{{ csrf_token() }}" />
    
    • 在body的script标签里设置window.Laravel.csrfToken
    window.Laravel = {
        csrfToken: '{{ csrf_token() }}'
    };
    
    • 在body的script标签里设置$.ajaxSetup(为以后要用到的Ajax请求设置默认的值)
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
    });
    
    • 在需要直接提交(非ajax提交)的form标签里,设置_token
    {{ csrf_field() }}
    

    该模板标签会被blade模板语言编译成,示例:

    <input type="hidden" name="_token" value="5XioxfXUeJZfypAryNZ53SwTHbhjwIv0iqvrvBqa">
    

    至此,CSRF保护就设置完成了。
    但,我有个疑问,首次打开该网站的页面(GET请求),是没有_token、X-CSRF-TOKEN和X-XSRF-Token的,
    而web.php路由中间件CSRF保护会验证所有请求,那岂不是会打不开该页面?而事实上是可以打开的,于是,我找到了如下Laravel源码:
    Illuminate\Foundation\Http\Middleware\VerifyCsrfToken:

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     *
     * @throws \Illuminate\Session\TokenMismatchException
     */
    public function handle($request, Closure $next)
    {
        if (
            $this->isReading($request) ||
            $this->runningUnitTests() ||
            $this->inExceptArray($request) ||
            $this->tokensMatch($request)
        ) {
            return $this->addCookieToResponse($request, $next($request));
        }
    
        throw new TokenMismatchException;
    }
    
    /**
     * Determine if the HTTP request uses a ‘read’ verb.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function isReading($request)
    {
        return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
    }
    
    /**
     * Add the CSRF token to the response cookies.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Symfony\Component\HttpFoundation\Response  $response
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function addCookieToResponse($request, $response)
    {
        $config = config('session');
    
        $response->headers->setCookie(
            new Cookie(
                'XSRF-TOKEN', $request->session()->token(), Carbon::now()->getTimestamp() + 60 * $config['lifetime'],
                $config['path'], $config['domain'], $config['secure'], false
            )
        );
    
        return $response;
    }
    

    意思是,如果请求方式为HEAD、GET和OPTIONS,则发送名为XSRF-TOKEN的cookie,而不作CSRF验证。

    完!如果认为有用,可以考虑以赞赏的方式告诉我哦。

    参考资料

    -跨站请求伪造:
    https://zh.wikipedia.org/wiki/跨站请求伪造

    相关文章

      网友评论

        本文标题:Laravel上实现CSRF保护

        本文链接:https://www.haomeiwen.com/subject/dopvxxtx.html