生成CSRF令牌取决于日期时间

时间:2013-04-08 20:50:22

标签: php symfony token csrf

在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分钟到期并且它不是一个好的解决方案。我想,原因是时间已经改变了。

2 个答案:

答案 0 :(得分:1)

Symfony的Form组件在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')...部分)。

希望有所帮助!

答案 1 :(得分:-1)

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

{{ form_rest(form) }}