laravel - 为什么 _token 和 XSRF-TOKEN 在 Laravel 中不同?

标签 laravel csrf websecurity

我不明白为什么 AJAX 请求 (XSRF-TOKEN) 的 token 与 _token 不同正常形式使用。此外,它更长。为什么?为什么有 2 个 token 呢?为什么不使用一个对 ajax 和普通请求都相同的请求?

最佳答案

1 方法,2 技术
Laravel 使用 2 种不同的技术来防止 CSRF 攻击。
方法是一样的:

to send a token (CSRF or XSRF) to The Client and Client Have to return it back in following request


有两个步骤:
  • 服务器发送 token (获取表单)(CSRF 或 XSRF)
  • 客户端返回 token 作为 X token (发布表单)(X-CSRF 或 X-XSRF)

  • when you see an X- token its an client-replied that client sends with Post to the server


    我们有两种技术的原因不是这些使用不同的方法,
    这是因为 Web 应用程序客户端架构使用 2 种不同的架构:
  • 老式:服务器生成纯 html 并将其发送给客户端
  • 单页应用程序:客户端 SPA 框架(如 Vue、React、Angular)以 Json 或 Xml 格式发送和接收数据,并在 Dom 中创建适当的 Html

  • 现在CSRF-Protection Technics 适应这两种客户端架构如下:
    +-------------+-----------------+-----------+------------+
    | Client Arch | Protection Tech | Get Token | Post Token |
    +-------------+-----------------+-----------+------------+
    | old-fashion | sync-token      | CSRF      | X-CSRF     |
    | SPA         | cookie-header   | XSRF      | X-XSRF     |
    +-------------+-----------------+-----------+------------+
    
    机制说明
    1.服务器生成Token
    Laravel 创建一个 CSRF token (40 个字符)并将其存储在 session 中
    /**
         * Regenerate the CSRF token value.
         *
         * @return void
         */
        public function regenerateToken()
        {
            $this->put('_token', Str::random(40));
        }
    
    在 session 中生成并存储 token 后, token 将作为 CSRF 和 XSRF 发送给客户端
    客户端将决定使用它想要的任何东西。
    2.服务器向客户端发送 token
    对于老式(同步 token 技术)客户端,可以通过拨打 csrf_token() 以两种形式接收 CSRF token 。 Blade 中的辅助方法:
  • 在表格正文中:<input type='hidden' name='_token' value='{{csrf_token()}}' />
  • 在元标记中,Ajax 请求可以在其 header 中使用它

  • 以下是此辅助方法返回相应值的方式:
    /**
         * Get the CSRF token value.
         *
         * @return string
         *
         * @throws \RuntimeException
         */
        function csrf_token()
        {
            $session = app('session');
    
            if (isset($session)) {
                return $session->token();
            }
    
            throw new RuntimeException('Application session store not set.');
        }
    
    对于 cookie-header(SPA 框架)客户端框架(如 Angular)可以在 Cookie 中接收 XSRF token 因为 :

    there is no Html Form generating in the server which server can seed its hidden input in it. and The Way it can send its token to the client is sending it with cookie. (This method named XSRF)

    /**
         * 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(), $this->availableAt(60 * $config['lifetime']),
                    $config['path'], $config['domain'], $config['secure'], false, false, $config['same_site'] ?? null
                )
            );
    
            return $response;
        }
    
    Laravel 将 token 放在两个地方,因为它取决于客户端使用哪种方法,并期望客户端响应其中一种方法。
    3.客户端向服务器发送X-Token
    在客户端:
  • 老式 (X-CSRF):
  • 在发布数据中发布 token 或:
  • 像这样进行ajax调用:
  • `$.ajaxSetup({
               headers: {
              'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
               }
              });`
    
  • SPA 框架:这些框架将 Token 作为 X-XSRF-TOKEN 放在 Post Headers 中
  • 服务器检查 X token 与 session 中 token

  • 现在是 Laravel 检查 token 的时候了
    在 VerifyCSRFMiddleware 中,Laravel 检查请求是否应该检查它检查的 CSRF 保护 token :
    /**
         * 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) //compares request_token vs session_token
            ) {
                return tap($next($request), function ($response) use ($request) {
                    if ($this->shouldAddXsrfTokenCookie()) {
                        $this->addCookieToResponse($request, $response); //add cookie to response
                    }
                });
            }
    
            throw new TokenMismatchException('CSRF token mismatch.');
        }
    
    感兴趣的两行:
    $this->tokensMatch($request)
    
    $this->addCookieToResponse($request, $response);
    
    所以服务器可以放置的每个请求中都有多个数据:
  • html表单输入 _token (40 个字符) ( C SRF)
  • html 元标题 csrf-token (40 个字符) ( C SRF)
  • cookies XSRF-TOKEN (224 个字符) ( X SRF)

  • 并且客户端可以将多个数据发送到服务器作为对 token 的响应
  • 发布参数 _token (40 个字符) (X- C SRF)
  • http header X-CSRF-TOKEN (40 个字符) (X- C SRF)
  • http header X-XSRF-TOKEN (224 个字符) (X- X SRF)

  • Why in CSRF token are 40 chars and in XSRF are 224 chars ? We Will get to this a little bit latter


    Http 请求必须将 token 与上述之一匹配 X-Token
       /**
         * Determine if the session and input CSRF tokens match.
         *
         * @param  \Illuminate\Http\Request  $request
         * @return bool
         */
        protected function tokensMatch($request)
        {
            $token = $this->getTokenFromRequest($request);// it get token from request
    
            return is_string($request->session()->token()) &&
                   is_string($token) &&
                   hash_equals($request->session()->token(), $token); //checks if it is equal to session token or not
        }
    
    
    
    /**
         * Get the CSRF token from the request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @return string
         */
        protected function getTokenFromRequest($request)
        {
            $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');//check sync-token
    
            if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
                $token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
            }
    
            return $token;
        }
    
    要检查的第一个模式是同步 token ,来自客户端的 token 可以在 <input name='_token' /> 中或者如果从客户端中的 Ajax 方法调用请求,它可以在 Http Header 中。
    线
    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
    
    将检查它,如果可以检索,它将返回并通过 session_token 检查
    但是 if (! $tokenNULL它将检查 cookie-header 模式:
    获取 $header = $request->header('X-XSRF-TOKEN')从 header 并解密它,如果它需要解密
    $token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
    
    如果它在被添加到 cookie 之前已经被加密
    Cookie 加密

    This is The Reason That XSRF Token Could be 224chars : Cookie Encryption and you may disable cookie Encryption and make the XSRF Token 40 chars, Like The CSRF Token


    so The Difference was for The Cookie Encryption.


    Cookie 加密的必要性
    但是为什么 Cookie 需要加密?为什么 XSRF Coo​​kie 需要加密?
    一般来说,Laravel 会在 cookie 上存储一些数据,并且 cookie 可以被客户端修改。因为服务器不想修改客户端,所以 加密 Cookie .
    这可以配置为不加密CSRF Coo​​kie 因为它不受用户更改的影响,并且只会被 窃取。 cookie 劫持 哪种加密不会阻止此事件。

    The only Difference its make is to having to token (unencrypted and encrypted) for two CSRF Protection methods. So if attackers can access a cookie-stored (X-XSRF) Token (Since Hijacking > Cookie is much easier to hijacking runtime html and css with XSS ) it cannot be Abuse With sync-token mechanism. Since CSRF Attack with http-form parameter is easier since html can be in email or etc While Runnig Js is Less common.


    结论

    So if a client use old-fashion client architect . the cookie-header technic > ( XSRF stored in Cookie ) wont leave him with a data leak in cookie.


    可在此处找到有关此预防模式的更多信息:
    https://en.wikipedia.org/wiki/Cross-site_request_forgery#Prevention

    关于laravel - 为什么 _token 和 XSRF-TOKEN 在 Laravel 中不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64592253/

    相关文章:

    php - 干预图像 - 如何上传 MIME 类型为 : application/octet-stream 的图像

    html - 使用 CORS 的 CSRF

    java - 从 Angular 2 向 Spring 支持的 Java 应用程序发送 POST 请求时出现 CSRF 问题

    rest - 如何防止 SQL 注入(inject)并提高 REST API 的安全性?

    php - Laravel Eloquent 查询数据透视表

    php - 如何在 Guzzle http 中添加 header

    webpack - webpack-dev-server 的 allowedHosts 安全机制的目的是什么?

    javascript - 同源策略 : Why can't JS code make an HTTP request to its domain?

    node.js - Laravel Nodejs 套接字 io

    python - 如何覆盖Django登录