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)