【问题标题】:Generate CSRF token dependig on datetime根据日期时间生成 CSRF 令牌
【发布时间】:2013-03-31 02:28:15
【问题描述】:

在 Symfony 2 中是否存在某种方式在每次呈现表单时生成 CSRF 令牌? 在我的控制器中,我尝试了这样的事情:

$request = $this->get('request');
    if ($request->getMethod() != 'POST') {
        $csrf = $this->get('form.csrf_provider');
        $date= new \DateTime("now");
        $this->date = $date->format('Y-m-d H:i:s');
        $token = $csrf->generateCsrfToken($this->date);
    } elseif($request->getMethod() == "POST") {
        $csrf = $this->get('form.csrf_provider');
        $token = $csrf->generateCsrfToken($this->date);
    }

    $form =  $this->createFormBuilder()
    ....
    ->add('_token','hidden',array(
        'data' => $token
        ))->getForm();

    if ($request->getMethod() == 'POST') {
        $form->bindRequest($request);

        if ($form->isValid()) {
            DO something
        }
    }

所有时间都在我的请求权令牌哈希中。但是在 bindRequest 更改为从 parameters.ini 中的安全字符串生成的默认哈希之后,isValid 方法肯定返回 FALSE 响应。存在某种方式,如何调整?

编辑 作为对解谜者回答的回应,我编辑了我的控制器并创建了我的 CSRF 提供程序,但我仍然收到“CSRF 令牌无效。请尝试重新提交表单”错误。我的 CSRF 提供程序如下所示:

namespace My\Namespace;
use Symfony\Component\HttpFoundation\Session;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;

class MyCsrfProvider implements CsrfProviderInterface
{
    protected $session;
    protected $secret;
    protected $datetime;

    public function __construct(Session $session, $secret)
    {
        $this->secret = $secret;
        $this->datetime = new \DateTime('now');
        $this->session = $session;
    }

    public function generateCsrfToken($intention)
    {
        return sha1($this->secret.$intention.$this->datetime->format('YmdHis').$this->getSessionId());
    }

    public function isCsrfTokenValid($intention, $token)
    {
        return $token === $this->generateCsrfToken($intention);
    }

    protected function getSessionId()
    {
        return $this->session->getId();
    }
}

比我添加到 config.yml 服务类:

services:
form.csrf_provider: 
    class: My\Namespace\MyCsrfProvider
    arguments: [ @session, %kernel.secret% ]

控制器我改成这个:

//any form without _token field
    $form =  $this->createFormBuilder()
        ->add('field1')
        ->add('field2')->getForm()

        if ($this->getRequest()->getMethod() == 'POST') {

            $request = $this->getRequest();
            $form->bindRequest($request);

            if ($form->isValid()) {
                Do something
                return $this->redirect($this->generateUrl('somewhere'));
            }
        }

在我的哈希中不能在几秒钟内出现问题吗?因为如果我从日期时间格式中删除秒数,它可以工作,但有 1 分钟到期,这不是一个好的解决方案。我想,原因是时间变了。

【问题讨论】:

  • 真的没人给点提示吗?
  • 你能解决这个问题吗?

标签: php symfony token csrf


【解决方案1】:

Symfony 的Form 组件在表单事件中生成 CSRF 令牌。请参阅Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener 类,了解现有实现的工作原理。这意味着当您在表单上调用 bindRequest()(或 Symfony 2.2+ 中的 bind())时,该表单事件将被调用并覆盖您声明的令牌。

定义不同标记的正确方法是创建一个CsrfProvider,它基本上是您编写的实现Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface 的类。查看Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider 了解如何实现此功能的示例。

一旦你编写了你的​​类,在你的app/config/parameters.yml(或另一个配置文件,在哪里都没有关系)中,将form.csrf_provider.class参数设置为你的类的名称。

在所有这些之后,您应该能够从控制器中删除您编写的所有自定义代码,并且只需使用表单系统,就好像没有任何不同(即,删除 ->add('_token')... 部分)。

希望有帮助!

【讨论】:

  • 感谢您的回复。我编辑了我的问题并在那里添加了我的提供者,但仍然无法正常工作。
  • 这可能是因为拥有基于当前时间的 CSRF 令牌永远无法工作,除非您在某处记录初始时间戳并在验证令牌时重用它。例如,generateCsrfToken() 在您的表单首次构建和显示时会有所不同,然后在提交和绑定表单时会有所不同。换句话说,我认为您的课程“有效”,但您的初衷是有缺陷的。
  • @theunraveler 我也有同样的问题。不幸的是,我们的客户希望每个页面请求都生成一个 CSRF。您正在谈论将时间戳保存在某处。我只是有一些澄清:1.可以将其保存在会话变量中吗? 2.黑客从会话变量中读取“未编码”时间戳不是很容易吗? 3. 为什么不存储整个哈希令牌呢?
  • 很好,我想我找到了答案here 但是,我仍然非常感谢您的意见。 TIA
【解决方案2】:

你知道form_rest吗?将此添加到表单的底部以使其自动生成您的 CSRF 令牌:

{{ form_rest(form) }}

【讨论】:

  • 当然,我用的是rest,但是没有问题。 _token 隐藏字段正确呈现。但问题是在表单提交之后,在 bindRequest 方法期间发生了一些改变我在表单对象中的令牌的事情。它将其更改为默认值,正如我在问题中所写的那样,在 parameters.ini 中设置。
猜你喜欢
  • 2010-12-20
  • 2014-01-21
  • 2014-11-01
  • 2015-01-08
  • 2011-05-02
  • 1970-01-01
  • 1970-01-01
  • 2015-02-14
  • 2019-04-08
相关资源
最近更新 更多