2013-04-08 7 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 его изменение хэша по умолчанию, генерируемого из строки безопасности в параметрах.ini, и isValid метод возвращает, конечно, FALSE-ответ. Существует какой-то способ, как его настроить?

EDIT В ответ на 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% ] 

и контроллер я изменяю это:

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

Cant быть проблема в секундах в моем хеше? Если я удаляю секунды из формата даты, вы можете использовать его, но с истечением 1 мин, а это не очень хорошее решение. Я думаю, причина в том, что время изменилось.

+0

Неужели никто с некоторыми намеками? – gavec

+0

Вы могли решить это? – JohnnyQ

ответ

-1

Знаете ли вы, что form_rest? Добавьте это к нижней части формы, чтобы он автоматически генерировать токен CSRF: Form компонент

{{ form_rest(form) }} 
+0

Конечно, им пользоваться отдыхом, но проблем нет. Скрытое поле _token отображается правильно. Но проблема возникает после отправки формы, когда во время метода bindRequest произошло что-то, что изменило мой токен в объекте формы. Он меняет его на значение по умолчанию, которое устанавливается в параметрах.ini, как я писал в вопросе. – gavec

1

Symfony генерирует маркер CSRF в случае формы. См. Класс Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener о том, как работает существующая реализация. Это означает, что когда вы вызываете bindRequest() (или bind() в Symfony 2.2+) в своей форме, это событие формы вызывается и перезаписывает токен, который вы объявили.

Правильный способ определить другой токен - создать 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

+1

Возможно, это связано с тем, что токен CSRF, основанный на текущем времени, никогда не сможет работать, если вы не запишете начальную временную метку где-то и не будете повторно использовать ее при проверке токена.Например, 'generateCsrfToken()' будет отличаться, когда ваша форма будет сначала построена и отображена, а затем, когда форма будет отправлена ​​и привязана. Другими словами, я думаю, что ваш класс «работает», но ваше первоначальное намерение ошибочно. – theunraveler

+0

@theunraveler У меня тоже такая же проблема. К сожалению, наш клиент хочет, чтобы CSRF генерировал каждый запрос страницы. Вы говорили о том, чтобы сохранить временную метку где-то. У меня просто несколько разъяснений: 1. Было бы хорошо сохранить это в переменной сеанса? 2. Разве хакеры не могли бы читать «незашифрованную» временную метку из переменной сеанса? 3. Почему бы не сохранить весь хеш-маркер вместо этого? – JohnnyQ

Смежные вопросы