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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21501058/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:00:38  来源:igfitidea点击:

Form validation fails due missing CSRF

pythonformsflaskflask-wtforms

提问by Houman

A few days ago I have reset my local flask environment without having captured the dependencies via a pip freezebefore I deleted it. Hence I had to re-install the latest version of the entire stack.

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

Now out of the blue I am no longer able to validate with forms. Flask claims CSRF would be missing.

现在突然间我无法再使用表单进行验证。Flask 声称 CSRF 将丢失。

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

The first time I send a GetI retrieve an empty form.errorsas expected. Now I fill out the form and submit it and form.errorsis showing: {'csrf_token': [u'CSRF token missing']}

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

This is so strange. I wonder if Flask-WTF has changed and I am using it wrongly.

这太奇怪了。我想知道 Flask-WTF 是否发生了变化,我是否错误地使用了它。

I can clearly see the form.CSRF_tokenexists, so why is it claiming it was missing?

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

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

I never touched the working template, but I post it here nonetheless:

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

{% 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 %}

Is this a new bug?

这是一个新的错误吗?

UPDATE:

更新:

I have reinstalled everything and the problem persists.

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

As Martijn suggested, I am debugging into the the following method in flask_wtf:

正如 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'))

The last condition is raising the validation error.

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

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

And you were right the HMAC comparison fails....both values are in every time different.

你是对的 HMAC 比较失败......两个值每次都不同。

return hmac_compare == hmac_csrf

I have both SECRET_KEY and CSRF_SESSION_KEY in my config defined.

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

采纳答案by Martijn Pieters

The Flask-WTF CSRF infrastructure rejects a token if:

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

  • the token is missing. Not the case here, you can see the token in the form.

  • it is too old (default expiration is set to 3600 seconds, or an hour). Set the TIME_LIMITattribute on forms to override this. Probably not the case here.

  • if no 'csrf_token'key is found in the current session. You can apparently see the session token, so that's out too.

  • If the HMAC signature doesn't match; the signature is based on the random value set in the session under the 'csrf_token'key, the server-side secret, and the expiry timestamp in the token.

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

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

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

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

Having eliminated the first three possibilities, you need to verify why the 4th step fails. You can debug the validation in flask_wtf/csrf.pyfile, in the validate_csrf()function.

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

For your setup, you need to verify that the session setup is correct (especially if you don't use the default session configuration), and that you are using the correct server-side secret. The form itself could have a SECRET_KEYattribute set but is not stable across requests, or the app WTF_CSRF_SECRET_KEYkey has changed (the latter defaults to the app.secret_keyvalue).

对于您的设置,您需要验证会话设置是否正确(尤其是在您不使用默认会话配置的情况下),以及您使用的服务器端密码是否正确。表单本身可能有一个SECRET_KEY属性集,但在请求中不稳定,或者应用程序WTF_CSRF_SECRET_KEY键已更改(后者默认为app.secret_keyvalue)。

The CSRF support was added in version 0.9.0, do check out the specific CSRF protection documentationif you upgraded. The standard Flask-WTF Formclass includesthe CSRF token as a hidden field, rendering the hidden fields is enough to include it:

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

{{ form.hidden_tag() }}

回答by Houman

I finally found the problem after nearly a day working on it. :( Big thanks to Martijn though for his help.

经过将近一天的努力,我终于找到了这个问题。:( 非常感谢 Martijn 的帮助。

The actual problem lies in the way the latest flask_wtf.csrfis working. The makers have overhauled it completely.

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

You have to replace all {{form.hidden_tag()}}in your templates with <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>.

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

And you have now to enable CSRF protection explicitly by adding CsrfProtect(app).

您现在必须通过添加CsrfProtect(app).

The documentationis now obviously reflecting that, but I didn't know this has changed and was chasing ghosts.

文件现在显然是反映了,但我不知道这种情况已经改变,是追鬼。

Its a big problem with deprecated functionality without notifying the developer somehow. Anyone that upgrades now to the latest version, will be chasing ghosts like I did. But its also my fault not having taken a snapshot of my dependencies. Lesson learned the hard way.

在不以某种方式通知开发人员的情况下,不推荐使用的功能是一个大问题。现在升级到最新版本的任何人都会像我一样追逐鬼魂。但这也是我的错,没有对我的依赖项进行快照。惨痛的教训。

回答by louis_guitton

For me, the problem was not coming from Flask-WTF being badly configured, or a missing token. It was coming from the environment variables.

对我来说,问题不是来自 Flask-WTF 配置不当或缺少令牌。它来自环境变量

If your Flask server is not running on localhost then in order to get Flask to work properly, you need to set a SERVER_NAMEenvironment variable. You've likely forgotten to modify the SERVER_NAMEvalue somewhere.

如果你的 Flask 服务器没有在本地主机上运行,​​那么为了让 Flask 正常工作,你需要设置一个SERVER_NAME环境变量。您可能忘记在SERVER_NAME某处修改该值。

For example, you could have something like this in config/settings.py:

例如,你可以有这样的东西config/settings.py

SERVER_NAME = 'my-domain.com'

For more information, check out this great resource

有关更多信息,请查看这个很棒的资源

回答by caverac

At the time of creating the app:

在创建应用程序时:

from flask_wtf.csrf import CsrfProtect

csrf = CsrfProtect()

app = Flask(__name__)   

...

csrf.init_app(app)

...