由于缺少 CSRF,表单验证失败

新手上路,请多包涵

几天前,我重置了我的本地烧瓶环境,但在删除它之前没有通过 pip freeze 捕获依赖项。因此我不得不重新安装整个堆栈的最新版本。

现在出乎意料的是,我无法再使用表单进行验证。 Flask 声称 CSRF 会丢失。

 def register():
    form = RegisterForm()
    if form.validate_on_submit():
       ...
    return make_response("register.html", form=form, error=form.errors)

我第一次发送 Get 时,我按预期检索了一个空的 form.errors 。现在我填写表格并提交 form.errors 显示: {'csrf_token': [u'CSRF token missing']}

这太奇怪了。我想知道是不是 Flask-WTF 变了,我用错了。

我可以清楚地看到 form.CSRF_token 存在,那么为什么它声称它丢失了?

 CSRFTokenField: <input id="csrf_token" name="csrf_token" type="hidden" value="1391278044.35##3f90ec8062a9e91707e70c2edb919f7e8236ddb5">

我从来没有碰过工作模板,但我还是把它贴在这里:

 {% from "_formhelpers.html" import render_field %}
{% extends "base.html" %}
{% block body %}
<div class="center simpleform">
    <h2>Register</h2>
    {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
    <form class="form-signin" action="{{ url_for('register') }}" method=post>
        {{form.hidden_tag()}}
        <dl>
            {{ render_field(form.name) }}
            {{ render_field(form.email) }}
            {{ render_field(form.password) }}
            {{ render_field(form.confirm) }}
            <dd><input type=submit value=Register class='btn btn-primary'>
        </dl>
    </form>
</div>
{% endblock %}

这是一个新错误吗?

更新:

我已经重新安装了所有东西,但问题仍然存在。

正如 Martijn 所建议的,我正在调试 flask_wtf 中的以下方法:

 def validate_csrf_token(self, field):
        if not self.csrf_enabled:
            return True
        if hasattr(request, 'csrf_valid') and request.csrf_valid:
            # this is validated by CsrfProtect
            return True
        if not validate_csrf(field.data, self.SECRET_KEY, self.TIME_LIMIT):
            raise ValidationError(field.gettext('CSRF token missing'))

最后一个条件是引发验证错误。

 field.data = "1391296243.8##1b02e325eb0cd0c15436d0384f981f06c06147ec"
self.SECRET_KEY = None (? Is this the problem)
self.TIME_LIMIT = 3600

你是对的,HMAC 比较失败了……这两个值每次都不同。

 return hmac_compare == hmac_csrf

我在配置中定义了 SECRET_KEY 和 CSRF_SESSION_KEY。

原文由 Houman 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 774
2 个回答

如果出现以下情况,Flask-WTF CSRF 基础设施将拒绝令牌:

  • 令牌丢失。这里不是这种情况,您可以在表单中看到令牌。

  • 它太旧了(默认到期时间设置为 3600 秒,或一个小时)。在表单上设置 TIME_LIMIT 属性以覆盖它。这里可能不是这种情况。

  • 如果在当前会话中没有找到 'csrf_token' 密钥。您显然可以看到会话令牌,所以它也出来了。

  • 如果 HMAC 签名不匹配;签名基于会话中在 'csrf_token' 密钥、服务器端秘密和令牌中的到期时间戳下设置的随机值。

排除了前三种可能性后,您需要验证第 4 步失败的原因。您可以在 flask_wtf/csrf.py 文件中调试验证,在 validate_csrf() 函数中。

对于您的设置,您需要验证会话设置是否正确(特别是如果您不使用默认会话配置),并且您使用的是正确的服务器端机密。表单本身可能有一个 SECRET_KEY 属性集,但在请求中不稳定,或者应用程序 WTF_CSRF_SECRET_KEY 键已更改(后者默认为 app.secret_key) .

CSRF 支持是在 0.9.0 版本中添加的,如果您升级了,请查看具体的 CSRF 保护文档。标准 Flask-WTF Form包含 CSRF 令牌作为隐藏字段,渲染隐藏字段足以包含它:

 {{ form.hidden_tag() }}

原文由 Martijn Pieters 发布,翻译遵循 CC BY-SA 3.0 许可协议

经过将近一天的工作,我终于找到了问题所在。 :( 非常感谢 Martijn 的帮助。

实际问题在于最新的 flask_wtf.csrf 的工作方式。制造商已对其进行了彻底检修。

您必须将模板中的所有 {{form.hidden_tag()}} 替换为 <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>

您现在必须通过添加 CsrfProtect(app) 来显式启用 CSRF 保护。

文档 现在显然反映了这一点,但我不知道这已经改变并且正在追逐幽灵。

在没有以某种方式通知开发人员的情况下,已弃用的功能是一个大问题。现在升级到最新版本的任何人都会像我一样追逐幽灵。但这也是我的错,没有拍摄我的依赖关系的快照。教训是艰难的。

原文由 Houman 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题