要求在所有请求中都存在X-CSRF-Token标头

时间:2014-03-08 00:14:14

标签: php symfony http-headers csrf

Symfony中的默认CSRF防护是基于表单的(如果您使用提供的表单构建器,则会自动发生)。但是,对于AJAX请求,手动将CSRF令牌附加到每个HTTP请求然后手动检查每个请求是很繁琐的。

一种好的方法是将令牌作为HTTP标头嵌入,如this question的注释中所建议的那样。然后可以将jQuery配置为在每个请求中包含此标头described here

我的问题是如何在Symfony中最好地处理这个问题?我可以使用Twig在每个页面中包含CSRF令牌,但是如何检查传入的请求以确保每个请求在标题中包含有效的令牌?

我知道如何使用控制器内的$request->headers->get('X-CSRF-Token')来访问HTTP标头,但这仍然意味着必须在每个控制器中单独执行此检查。我应该在下落中添加这张支票,以便尽早发现它?

1 个答案:

答案 0 :(得分:0)

我不是100%肯定,但我会使用BaseFormType并扩展它。

您的基本表单类型将使用表单事件PRE_SET_DATA侦听器,并在请求中查找标题,然后将该标题填充到_token字段中。

//FormType
class BaseFormType extends AbstractType
{
    protected $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $request = $this->request;
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use($request){
            $token = $request->headers->get('X-CSRF-Token');
            if($token){
                $form = $event->getForm();
                $form->get('_token')->setData($token);
            }
        }
    }
}

然后所有的FormTypes都将扩展它:

//YourCustomFormType
class YourCustomFormType extends BaseFormType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm();
        $form->add('name');
    }
}

然后你的控制器动作

//Controller
public function sayMyNameAction(Request $request)
{
    $name = new Name();
    $form = $this->createForm(new YourCustomFormType($request),$name);
    if($request->isMethod('POST'))
    {
        $form->handleRequest($request);
        if($form->isValid()){
            return new JsonResponse(array('say':$name->getName()));
        }
    }
}

或类似的东西。