1

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

4

2 回答 2

1

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

希望有帮助!

于 2013-04-10T06:03:49.180 回答
-1

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

{{ form_rest(form) }}
于 2013-04-08T21:34:17.323 回答