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
Form validation fails due missing CSRF
提问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.py在validate_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)
...

