2013-04-08 68 views
1

在Symfony 2中存在某种方式在每次呈现表单时生成CSRF令牌? 在我的控制器我想是这样的:生成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响应。存在某种方式,如何调整它?

编辑 针对theunraveler答案,我编辑我的控制器,并创建我的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% ] 

和控制器I改成这样:

//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分钟到期并不是一个好的解决方案。我想,原因是时间发生了变化。

+0

真的没人提供一些提示吗? – gavec 2013-04-09 21:00:53

+0

你能解决这个问题吗? – JohnnyQ 2015-01-28 12:39:51

回答

-1

你知道吗form_rest?添加到您的底部形态,系统将自动生成你的CSRF令牌:

{{ form_rest(form) }} 
+0

当然,即时消息使用休息,但没有问题。 _token隐藏字段呈现正确。但问题是在表单提交之后,当bindRequest方法发生了什么改变了我的表单对象的令牌。它会将其更改为默认值,这是在写有问题时在parameters.ini中设置的。 – gavec 2013-04-08 22:02:14

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')...部分)。

希望有帮助!

+0

感谢您的回复。我编辑了我的问题,并在那里添加了我的提供者,但仍然无法正常工作。 – gavec 2013-04-10 10:14:55

+1

这可能是因为拥有基于当前时间的CSRF令牌永远不能工作,除非您在某处记录初始时间戳并在验证令牌时重用它。例如,当您的表单首先被构建并显示时,然后当表单被提交并绑定时,'generateCsrfToken()'将会有所不同。换句话说,我认为你的课程“有效”,但你的初衷是有缺陷的。 – theunraveler 2013-04-10 14:22:31

+0

@theunraveler我也有同样的问题。不幸的是,我们的客户希望有一个CSRF生成每个页面请求。你在谈论保存时间戳的地方。我只是有几点澄清: 1.将它保存在会话变量中可以吗? 2.黑客会不会轻易从会话变量中读取“未编码”的时间戳? 3.为什么不存储整个哈希令牌? – JohnnyQ 2015-01-28 16:56:27