【问题标题】:How can I check whether the supplied CSRF token is invalid in Symfony2?如何检查提供的 CSRF 令牌在 Symfony2 中是否无效?
【发布时间】:2013-02-09 06:21:29
【问题描述】:

我创建了一个 Symfony2 表单并将其绑定到请求。在继续处理表格的其余部分之前,我需要明确确保 CSRF 令牌是否有效/无效。

$form['_token']->isValid() 抛出 OutOfBoundsException 并显示消息“子 _token 不存在。”

我仍然可以验证呈现的表单是否包含_token 字段。如果 CSRF 值无效,$form->isValid() 返回 false。

我在这里错过了什么?


更新 1:

控制器(部分):

private function buildTestForm() {
    $form = $this->createFormBuilder()
            ->add('name','text')
            ->getForm();
    return $form;
}

/**
 * @Route("/test/show_form", name="test.form.show")
 * @Method("GET")
 */
public function showFormTest()
{
    $form = $this->buildTestForm();
    return $this->render('TestBundle::form_test.html.twig', array('form' => $form->createView()));
}

/**
 * @Route("/test/submit_form", name="test.form.submit")
 * @Method("POST")
 */
public function formTest()
{
    $form = $this->buildTestForm();
    $form->bind($this->getRequest());
    if ($form['_token']->isValid()) {
        return new Response('_token is valid');
    } else {
        return new Response('_token is invalid');
    }
}

模板

{# Twig template #}
<form action="{{ path('test.form.submit') }}" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}
    <input type="submit" name="go" value="Test Form" />
</form>

【问题讨论】:

  • 您至少缺少表单代码。
  • @mpm, true - 现在添加了完整的代码来重现问题。
  • @artworkadshi 是的,我知道这一点。问题是如果 _token 无效,我需要做出不同的反应。这就是为什么我需要明确检查它。

标签: validation symfony csrf symfony-forms


【解决方案1】:

没有记录的手动检查 csrf 令牌的方法。 Symfony 自动验证这个令牌的存在和准确性。 http://symfony.com/doc/current/book/forms.html#csrf-protection

但是有一个 csrf 提供者:

http://api.symfony.com/2.0/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.html

和

http://api.symfony.com/master/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.html

标记类能够提供 CSRF 保护你可以生成一个 CSRF 使用方法 generateCsrfToken() 生成令牌。对这个方法你 应该传递一个对应该保护的页面唯一的值 对抗 CSRF 攻击。这个值不一定是 秘密。该接口的实现负责添加 更多秘密信息。

如果您想保护表单提交免受 CSRF 攻击,您 可以提供一个“意图”字符串。这样,您可以确保 表单只能绑定到旨在处理表单的页面, 也就是说,使用相同的意图字符串来验证 CSRF 令牌 使用 isCsrfTokenValid()。

你可以像这样检索提供者

$csrf = $this->get('form.csrf_provider');

使用然后可以使用

public Boolean isCsrfTokenValid(string $intention, string $token)

Validates a CSRF token.

参数 string $intention 生成时使用的意图 CSRF token string $token 浏览器提供的token

返回值 Boolean 浏览器提供的令牌是否为 正确

你需要找出你的表单使用的意图字符串。

关于 SO 的一些有趣的帖子:

Symfony CSRF and Ajax

Symfony2 links with CSRF token

【讨论】:

【解决方案2】:

我不确定,但您是否在未映射的字段上尝试过验证器:

$violations = $this->get('validator')->validatePropertyValue($entity, '_token', $form['_token']);
if (count($violations)) {
    // the property value is not valid
}

【讨论】:

    【解决方案3】:

    除了artworkad,你还可以指定意图:

    树枝:

    <form method="post">
        <input type="text" name="form_name[field]" value="" />
        <input type="hidden" name="_csrf_token" value="{{ csrf_token('form_name') }}">
    </form>
    

    PHP:

    $token = $request->request->get('_csrf_token');
    $csrf_token = new CsrfToken('form_name', $token);
    var_dump($this->get('security.csrf.token_manager')->isTokenValid($csrf_token));
    

    或者不:

    树枝:

    <form method="post">
        <input type="text" name="field" value="" />
        <input type="hidden" name="_csrf_token" value="{{ csrf_token('') }}">
    </form>
    

    PHP:

    $token = $request->request->get('_csrf_token');
    $csrf_token = new CsrfToken('', $token);
    var_dump($this->get('security.csrf.token_manager')->isTokenValid($csrf_token));
    

    【讨论】:

    • ajax请求是否应该重新生成令牌?
    • 您可以在 ajax 中重新生成令牌并更新您的输入值:public function tokenAction($intention) { $csrf = $this-&gt;get('security.csrf.token_manager'); $token = $csrf-&gt;refreshToken($intention); return new Response($token); }
    【解决方案4】:

    另一种方式(如果您更喜欢 DI)在控制器之外(例如在侦听器中)验证令牌:

    /** @var Symfony\Component\Security\Csrf\CsrfTokenManagerInterface */
    private $csrfTokenManager;
    
    public function __construct(CsrfTokenManagerInterface $csrfTokenManager) 
    {
        $this->csrfTokenManager = $csrfTokenManager;
    }
    
    public function isValid(): bool
    {
        return $this->csrfTokenManager->isTokenValid(
                new CsrfToken('authenticate', $request->getRequest()->headers->get('X-CSRF-TOKEN'))
            );
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-25
      • 2012-12-15
      • 2015-08-04
      • 1970-01-01
      • 2012-02-03
      • 2017-08-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多