Symfony CSRF 和 Ajax

Posted

技术标签:

【中文标题】Symfony CSRF 和 Ajax【英文标题】:Symfony CSRF and Ajax 【发布时间】:2012-08-16 18:34:58 【问题描述】:

我正在尝试在我的 Symfony 2 项目中实现一些 ajax 功能。使用 jquery 的 $.post 我想将一些数据发送回我的控制器。但是,当我只是 POST 数据时,没有 CSRF 保护,因为 symfony 的 csrf 保护似乎只适用于表单。

什么是实现这一点的非常简单的方法?

使用表单时,我只需执行 $form->isValid() 即可确定 CSRF 令牌是否通过。我目前正在将我想要发布的所有内容放在一个表单中,然后发布。这基本上意味着我只使用该表单来实现 CSRF 保护,这看起来很 hacky。

【问题讨论】:

不是所有其他页面都只是用户可以访问的页面吗?这意味着通常的 security.yml 设置会阻止应该看到它的人看到它。 【参考方案1】:

你应该试试这个 sn-p。 Symfony 表单应该生成特殊的 _csrf_token 应该与 post 请求一起发送。如果没有此值,将引发安全警报。

当然#targetForm应该被form id和/endpoint替换成目标ajax url

$('#targetForm').bind('submit', function(e) 

    e.preventDefault();
    var data = $(this).serialize();

    $.post('/endpoint', data, function(data) 
        // some logic here
    );

);

【讨论】:

是的,但我试图不为我想要使用的每一位 ajax 使用表单。仍然是通过 ajax 发送表单的好选择!【参考方案2】:

在 Symfony2 中,CSRF 令牌默认基于会话。如果你想生成它,你只需要得到这个服务和调用生成方法:

//Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider by default
$csrf = $this->get('form.csrf_provider');
//Intention should be empty string, if you did not define it in parameters
$token = $csrf->generateCsrfToken($intention); 

return new Response($token);

This question 可能对你有用

【讨论】:

好了,问题解决了一半。现在我需要验证是否 POST 的令牌,我会这样做:api.symfony.com/2.0/Symfony/Component/Form/Extension/Csrf/… 现在我需要将令牌返回到模板中。由于这篇文章是由一个成熟的 html 页面制作的,我可以将它放在一个带有 CSS 隐藏属性的 div 中,并让该页面上的每个启用 ajax 的脚本都使用它。但由于令牌仅有效一次,我将不得不让每个 ajax POST 返回一个新令牌并将其设置回隐藏的 div。现在,如果发出请求并且响应超时,服务器端将生成一个新令牌,但不会设置它。不过,这种情况发生的可能性很小。我会接受这个作为答案,尽管它仍然感觉很hacky。 @peterrus 好吧...如果您对这种原生行为不满意 - 您可以编写自己的 csrf_provider 并实现您需要的行为 :) 对于嵌入令牌:设置 X-CSRF-Token 标头似乎是一个巧妙的解决方案:erlend.oftedal.no/blog/?blogid=118 在 Symfony 3 中服务的名称被更改为security.csrf.token_manager。接口也发生了变化。您需要致电 getToken() 而不是 generateCsrfToken()【参考方案3】:

我有这个问题,间歇性的。原来这不是因为我的 ajax,而是因为 Silex 为您提供了一个已弃用的DefaultCsrfProvider,它使用会话 ID 本身作为令牌的一部分,为了安全起见,我随机更改了 ID。相反,明确告诉它使用新的CsrfTokenManager 来修复它,因为它会生成一个令牌并将其存储在 会话中,以便会话 ID 可以更改而不会影响令牌的有效性。

/** Use a CSRF provider that does not depend on the session ID being constant. We change the session ID randomly */
$app['form.csrf_provider'] = $app->share(function ($app) 
    $storage = new Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage($app['session']);
    return new Symfony\Component\Security\Csrf\CsrfTokenManager(null, $storage);
);

【讨论】:

【参考方案4】:

在 Symfony 4+ 中,您可以在控制器或操作或任何地方使用依赖注入,例如,如果您正在提交表单并希望刷新同一表单的令牌,$tokenId 是表单类型类:

namespace App\Controller;

use App\Form\MyFormType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

class MyController extends AbstractController

    public function submit(CsrfTokenManagerInterface $tokenManager): JsonResponse
    
        // ...
        $token = $tokenManager->refreshToken(MyFormType::class);
        return new JsonResponse(['token' => $token->getValue()]);
    

在您的 javascript 中,您可以更新现有的令牌 <input>

const token = document.getElementById('_token');
fetch(url, opts)
    .then(resp => resp.json())
    .then(response => 
        if (response.token) 
            token.value = response.token;
        
    );

【讨论】:

以上是关于Symfony CSRF 和 Ajax的主要内容,如果未能解决你的问题,请参考以下文章

带有 CSRF 令牌和会话的 Symfony 4 功能形式测试

VueJs 前端的无效 CSRF 令牌错误(symfony 5)

symfony2 CSRF 无效

Symfony2 注销 CSRF 保护:csrf_provider 无法识别

Symfony2:记住我身份验证时无效的 CSRF 令牌

Symfony:身份验证请求失败:无效的 CSRF 令牌