Laravel CSRF令牌

21

编辑:我应该在一开始说,我正在使用AngularJS在前端,并通过XHR进行所有请求。我正在开发一个应用程序,为每个用户请求使用CSRF令牌。

每次请求后我是否应该重新生成令牌

类似于:

Session::forget("_token") and Session::put("_token", RANDOM_SOMETHING)

每个用户使用相同的会话是否足够?

这样做有什么好处吗?


你是用这个令牌做什么的?假设你使用它来防止跨站脚本攻击以外的其他用途,很难说它是否足够“好”来适应你的情况。 - Sam
@itachi Laravel 的 CSRF 令牌用于防止跨站点请求(通常是 XSS)。它是保存在网站会话中并与每个表单提交一起发送的令牌,因此必须从具有会话的网站提交表单以获得正确的会话...而不是通过跨站点脚本伪造请求。 - Sam
2
@Sam 是的。但CSRF和XSS是两个非常不同的方面。有一个令牌会帮助你在CSRF中,但不会在XSS中。 - itachi
@itachi 好的,谢谢你指出来。我的解释很差,但我认为我们仍然需要知道OP使用CSRF令牌来保护他的应用程序的意图。 - Sam
6个回答

40

使用Laravel 5的Blade模板,这很容易。

如果你只想要csrf令牌的值,可以通过编写以下代码生成:

{{ csrf_token() }}

生成的令牌值如下:

7YC0Sxth7AYe4RFSjzaPf2ygLCecJhPbyXhz6vvF
如果您正在使用表单,您可以在表单内添加以下代码行:
{{ csrf_field() }}

这将生成像这样的 HTML:

<input type="hidden" name="_token" value="7YC0Sxth7AYe4RFSjzaPf2ygLCecJhblahblah">

16

Laravel应该已经为您处理了这个问题,您不需要管理_token的创建/删除

<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
请参阅此处文档中的“CSRF保护”部分:http://laravel.com/docs/security

我在前端使用AngularJS,在每个XHR请求中发送CSRF令牌,但我没有按照常规方式处理它。 - Gabriel Matusevich
2
我尝试使用Session :: token()和其他方法来检索令牌,但是Laravel没有重新生成令牌,我的问题是我应该重新生成令牌还是在整个会话中只使用相同的令牌。 - Gabriel Matusevich

10
如果您正在使用Laravel 5.6,那么在表单顶部执行以下操作,以创建用于CSRF令牌的隐藏输入字段。
  @csrf

7
取决于情况。如果攻击者不是MITM,也就是说他们无法窃听您的Web应用程序和API服务器之间的流量,那么单个CSRF令牌对于整个会话应该足够了。
假设您在服务器端也保护敏感操作(即仅允许资源所有者访问资源,例如“删除我的帐户”等),令牌将确保发出请求的浏览器是合法的,已认证用户的浏览器。我认为这就是您所要担心的全部问题。
另一方面,如果攻击者能够查看Web应用程序与API之间的非安全流量,他们可能会获取CSRF令牌和您的session_id,并透明地执行恶意操作。在这种情况下,对于每个请求(POST或任何进行敏感操作的类型),授予,使用并随后丢弃一个令牌只会使攻击者的工作稍微困难一些,但你仍然注定要失败。
以上仅供参考。

1
这个答案应该被询问者接受,因为它是唯一一个真正尝试回答他的问题的答案。 - Gustavo Straube
@hlev,你说得非常正确。那么你有什么建议? - rameezmeans
1
@r89human 和以前一样。对于大多数目的来说,TLS(HTTPS)和单个令牌用于用户会话应该足够了。有关更全面的概述,请参阅OWASP备忘单。https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#introduction - hlev

3

CSRF token(跨站点请求伪造)通过比较cookie token和服务器token来防止跨站攻击。

您可以通过使用csrf_token()助手函数在laravel中生成csrf token。如果您想要完整的csrf字段,则可以使用csrf_field()函数,其内部逻辑是

function csrf_field()
{
   return new HtmlString('<input type="hidden" name="_token" value="'.csrf_token().'">');
}

每次产生新的请求时,Laravel都会生成一个随机令牌,并将其存储在浏览器cookie和会话中。存储后,它们会相互比较,例如 cookie == session token

Laravel内部逻辑如下,您可以在VerifyCsrfToken中间件中找到它。

/**
 * Determine if the session and input CSRF tokens match.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function tokensMatch($request)
{
    $token = $this->getTokenFromRequest($request);

    return is_string($request->session()->token()) &&
           is_string($token) &&
           hash_equals($request->session()->token(), $token);
}

/**
 * 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');

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

    return $token;
}

/**
 * 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;
}

3
好的答案,附带示例。谢谢! - user7937158

2
如果您想在控制器中获取CSRF令牌,可以像这样使用它,并重定向post路由。
$CSRFToken = csrf_token();

易如反掌,希望能对您有所帮助


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接