CSRF 令牌无效。请尝试重新提交表单

Posted

技术标签:

【中文标题】CSRF 令牌无效。请尝试重新提交表单【英文标题】:The CSRF token is invalid. Please try to resubmit the form 【发布时间】:2014-06-20 18:28:06 【问题描述】:

每次我尝试提交表单时都会收到此错误消息:

我的表单代码是这样的:

<form novalidate action="path('signup_index')" method="post" form_enctype(form) role="form" class="form-horizontal">
    <div class="form-group">
         form_label(form.email, 'Email', 'label_attr': 'class': 'col-md-1 control-label') 
         form_widget(form.email, 'attr': 'class': 'col-md-2') 
         form_errors(form.email) 
    </div>

    <div class="form-group">
         form_label(form.nickname, 'Nickname', 'label_attr': 'class': 'col-md-1 control-label') 
         form_widget(form.nickname, 'attr':'class': 'col-md-2') 
         form_errors(form.nickname, 'attr': 'class': 'col-md-3') 
    </div>
    <div class="form-group">
         form_label(form.password, 'password', 'label_attr': 'class': 'col-md-1 control-label') 
         form_widget(form.password, 'attr': 'class': 'col-md-2') 
         form_errors(form.password, 'attr': 'class': 'col-md-3') 
    </div>

    <div class="form-group">
         form_label(form.password_repeat, 'Repeat password', 'label_attr': 'class': 'col-md-1 control-label') 
         form_widget(form.password_repeat, 'attr':'class': 'col-md-2') 
         form_errors(form.password_repeat, 'attr': 'class': 'col-md-3') 
    </div>
    <div class="form-group">
        <div class="col-md-1 control-label">
        <input type="submit" value="submit">
    </div>

    </div>
</form>

有什么想法吗?

【问题讨论】:

@MKhalidJunaid 你为什么要保护这个? 【参考方案1】:

您需要在表单中添加_token,即

 form_row(form._token) 

到目前为止,您的表单缺少 CSRF 令牌字段。如果您使用 twig 表单函数来呈现您的表单,例如 form(form),这将自动为您呈现 CSRF 令牌字段,但您的代码显示您正在使用原始 html 呈现表单,例如 &lt;form&gt;&lt;/form&gt;,因此您必须手动呈现字段。

或者,只需在表单的结束标记前添加 form_rest(form)

根据文档

这会渲染给定的所有尚未渲染的字段 形式。始终将其放在表单中的某个地方是个好主意 因为它会为您呈现隐藏字段并制作您忘记的任何字段 渲染得更明显(因为它会为您渲染该字段)。

form_rest(view, variables)

【讨论】:

form_rest(form) 对我来说是最好的。谢谢,+1 @M Khalid Junaid 我有一个文件,并通过 symfony 表单组件呈现它。在树枝中获取令牌并在控制器中获取令牌值,但是总是得到相同的错误。 谢谢,在我的情况下,我有 form_end(form, 'render_rest': false) ,我之前为一些测试设置过。现在我把它改成普通的 form_end(form) ,它工作了。 如果您看到这篇文章,请记住该文档不包含此信息。我已经创建了 1 个 PR 来添加它,但我不知道我还能做些什么来帮助其他人,问候。 github.com/symfony/symfony-docs/pull/16321【参考方案2】:

当您的表单有很多元素时,您也会看到此错误消息。

php.ini 中这个选项导致问题的原因

; How many GET/POST/COOKIE input variables may be accepted
 max_input_vars = 1000

问题是_token字段错过了PUT(GET)请求,所以你必须增加值。

另外,它涉及一个大文件。增加

upload_max_filesize

选项会解决问题。

【讨论】:

在我的例子中是 post_max_size + upload_max_filesize 我只在提交包含大量元素的表单时遇到问题。 将 max_input_vars 设置为更大的数字也为我修复了它。当您考虑它时,它是有道理的 - CSRF 令牌通常是呈现和发送的最后一个表单元素。当您有太多元素时“从请求中截断更多输入变量。”。所以 CSRF 令牌在到达 Symfony 时就已经丢失了。 (来源:php.net/manual/en/info.configuration.php#ini.max-input-vars)。 在调试了这个 csrf 错误 4 小时后,我在这里找到了答案。我的表格太大,没有发送 csrf。谢谢 在我的例子中,表单本身看起来并不是很大,但是有一个关系字段,有 100 个关系。【参考方案3】:

这是因为表单默认包含 CSRF 保护,在某些情况下这不是必需的。

您可以在getDefaultOptions 方法中的表单类中禁用此 CSRF 保护,如下所示:

// Other methods omitted

public function getDefaultOptions(array $options)

    return array(
        'csrf_protection' => false,
        // Rest of options omitted
    );

如果您不想禁用 CSRF 保护,则需要在表单中呈现 CSRF 保护字段。可以通过在视图文件中使用 form_rest(form) 来完成,如下所示:

<form novalidate action="path('signup_index')" method="post" form_enctype(form) role="form" class="form-horizontal">
    <!-- Code omitted -->

    <div class="form-group">
        <div class="col-md-1 control-label">
            <input type="submit" value="submit">
        </div>

    </div>
     form_rest(form) 
</form>

form_rest(form) 会呈现您未手动输入的所有字段。

【讨论】:

“不必要”可能没问题,但有什么好的理由不保护每一个表格吗? @NicoHaase 是的,如果您构建无状态 API。您仍然可以使用 symfony 表单进行验证,并且不需要 CSRF 保护security.stackexchange.com/questions/166724/…【参考方案4】:

在你的&lt;/form&gt; 标签之前:

 form_rest(form) 

它会自动插入其他重要的(隐藏的)输入。

【讨论】:

【参考方案5】:

我遇到了一个奇怪的问题:清除浏览器缓存并没有解决问题,但清除 cookie(即 PHP 会话 ID cookie)确实解决了这个问题。

这必须在您检查了所有其他答案后完成,包括验证您确实在隐藏的表单输入字段中拥有令牌。

【讨论】:

【参考方案6】:

除了其他人的建议之外,如果您的会话存储不工作,您可能会收到 CSRF 令牌错误。

在最近的一个案例中,我的一位同事将 'session_prefix' 更改为一个包含空格的值。

session_prefix: 'My Website'

这破坏了会话存储,这反过来意味着我的表单无法从会话中获取 CSRF 令牌。

【讨论】:

一个相关的陷阱使您的控制器中的会话无效。【参考方案7】:

如果您已将表单从纯 HTML 转换为 twig,请确保您没有错过删除结束 &lt;/form&gt; 标记。愚蠢的错误,但我发现这可能是导致此问题的原因。

当我收到这个错误时,一开始我无法弄清楚。我使用form_start()form_end() 来生成表单,所以我不应该用form_row(form._token) 显式添加令牌,或者使用form_rest() 来获取它。 It should have already been added automatically by form_end().

问题是,我正在使用的视图是我从纯 HTML 转换为 twig 的视图,我错过了删除结束 &lt;/form&gt; 标记,所以不是:

 form_end(form) 

我有:

</form>
 form_end(form) 

这实际上看起来可能会引发错误,但显然不会,所以当form_end() 输出form_rest() 时,表单已经关闭。表单实际生成的页面源是这样的:

<form>
    <!-- all my form fields... -->
</form>
<input type="hidden" id="item__token" name="item[_token]" value="SQAOs1xIAL8REI0evGMjOsatLbo6uDzqBjVFfyD0PE4" />
</form>

显然解决方案是删除多余的结束标签,然后再喝点咖啡。

【讨论】:

【参考方案8】:

我最近遇到了这个错误。原来我的 cookie 设置在 config.yml 中不正确。将cookie_pathcookie_domain 设置添加到framework.session 修复了它。

【讨论】:

【参考方案9】:

我最近也遇到了同样的问题,我的情况在这里还没有提到:

问题是我在localhost 域上测试它。我不确定这到底是为什么,但在我将localhost 的主机名别名添加到/etc/hosts 之后,它就开始起作用了:

127.0.0.1        foobar

在使用 Apache 和 localhost 作为域时,会话可能有问题。如果有人可以在 cmets 中详细说明,我很乐意编辑此答案以包含更多详细信息。

【讨论】:

【参考方案10】:

如果您不想使用 form_row 或 form_rest 并且只想访问 _token 在您的 twig 模板中的值。使用以下内容:

<input type="hidden" name="form[_token]" value=" form._token.vars.value " />

【讨论】:

如果您这样做,请确保始终调用 % do form._token.setRendered % 否则如果您在某处有 form_rest 调用,您可能会得到两个令牌输入字段【参考方案11】:

在我的例子中,我遇到了实体中的 maxSize 注释,因此我将它从 2048 增加到 20048。

 /**
 * @Assert\File(
 *     maxSize = "20048k",
 *     mimeTypes = "application/pdf", "application/x-pdf",
 *     mimeTypesMessage = "Please upload a valid PDF"
 * )
 */
private $file;

希望这个答案有帮助!

【讨论】:

【参考方案12】:

我遇到了类似的问题。在确保实际呈现了令牌字段(请参阅接受的答案)后,我检查了我的 cookie。 我的 Chrome 浏览器中的域有 2(!) 个 cookie,显然是因为我在与另一个应用程序相同的域上运行该应用程序,但使用不同的端口(即 mydomain.com 在有问题的应用程序运行时设置原始 cookie在 mydomain.com:123) 现在显然 Chrome 发送了错误的 cookie,因此 CSRF 保护无法将令牌链接到正确的会话。

修复:清除相关域的所有 cookie,确保您不在同一个域上使用不同端口运行多个应用程序。

【讨论】:

【参考方案13】:

我遇到了同样的错误,但在我的情况下,问题是我的应用程序使用了多个一级域,而 cookie 使用了一个。从config.yml 中的framework.session 中删除cookie_domain: ".%domain%" 会导致cookie 默认为表单所在的任何域,从而解决了问题。

【讨论】:

【参考方案14】:

你需要记住 CSRF 令牌是存储在 session 中的,所以这个问题也可能是由于无效的 session 处理而发生的。如果您在本地主机上工作,请检查例如如果会话 cookie 域设置正确(在 PHP 中,在本地主机上应该为空)。

【讨论】:

【参考方案15】:

这似乎是使用引导程序时的一个问题,除非您通过 form(form) 呈现表单。此外,问题似乎只发生在输入类型=“隐藏”上。如果您使用表单检查页面,您会发现隐藏的输入根本不是标记的一部分,或者它正在呈现但由于某种原因未提交。如上所述,添加 form_rest(form) 或像下面这样包装输入应该可以解决问题。

<div class="form-group">
    <input type="hidden" name="_csrf_token" value=" csrf_token('authenticate') ">
</div>

【讨论】:

以上是关于CSRF 令牌无效。请尝试重新提交表单的主要内容,如果未能解决你的问题,请参考以下文章

Symfony3 中的错误“CSRF 令牌无效。请尝试重新提交表单”

Ajax GET 请求后表单提交失败(无效的 CSRF 令牌)。怎么修?

打开新的浏览器选项卡会使 Django 的 CSRF 令牌无效,从而阻止表单提交

在测试中获取 CSRF 令牌

停止在身份验证时重新生成/使 CSRF 无效

PHP CSRF 表单令牌 + 验证建议