看起来像一个简单的错误-由于“ CSRF令牌丢失”错误而导致表单提交无法通过-变成了漫长的一天.我遍历了与Flask或Flask-WTF相关的所有SO文章,并且缺少CSRF令牌,似乎没有任何帮助.
详细信息如下:
遵循Martijin’s guidelines之前的问题:
The Flask-WTF CSRF infrastructure rejects a token if:
1) the token is missing. Not the case here, you can see the token in the form.
该令牌肯定存在于我的表单中,并且已成功过帐
2) it is too old (default expiration is set to 3600 seconds, or an hour).
Set the TIME_LIMIT attribute on forms to override this. Probably not the
case here.
对我来说也很好-令牌在默认的有效期内
3) if no ‘csrf_token’ key is found in the current session. You can
apparently see the session token, so that’s out too.
就我而言,Flask已正确设置并查看了session [‘csrf_token’]
4) 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.
这是我的问题.提交的表单的CSRF和会话CSRF之间的HMAC比较失败.但是我不知道如何解决.我已经绝望了(就像其他提问者一样),以深入研究Flask-WTF代码并设置调试消息以了解发生了什么.据我所知,它的工作方式如下:
1)“ form.py”(Flask-WTF)中的generate_csrf_token()要生成CSRF令牌.因此它调用:
2)“ csrf.py”中的generate_csrf().如果该会话不存在,则该函数将生成一个新会话[‘csrf_token’].就我而言,这总是会发生-尽管其他会话变量似乎在请求之间仍然存在,但调试显示,在请求开始时,会话中从未包含过“ csrf_token”.这正常吗?
3)当我在模板上渲染隐藏字段时,返回生成的令牌,并且大概将其合并到form变量中. (再次,调试显示此令牌以表单形式存在,并且已正确提交和接收)
4)接下来,提交表单.
5)现在,调用csrf.py中的validate_csrf.但是由于发生了另一个请求,并且generate_csrf()生成了新的会话CSRF令牌,所以这两个令牌的两个时间戳(在会话中和来自表单)将不匹配.而且由于CSRF部分由到期日期组成,因此验证失败.
我怀疑问题出在步骤2中,其中为每个请求都生成了一个新令牌.但是我不知道为什么会话中的其他变量会在请求之间持续存在,而不是在“ csrf_token”之间持续存在.
SECRET_KEY或WTF_CSRF_SECRET_KEY都没有奇怪(它们已正确设置).
有人有想法么?
解决方法:
我想到了.它似乎是一个cookie /会话限制(可能超出了Flask的控制范围),并且在达到该限制时会静默丢弃会话变量(这似乎更像是个错误).
这是一个例子:
templates / hello.html
<p>{{ message|safe }}</p>
<form name="loginform" method="POST">
{{ form.hidden_tag() }}
{{ form.submit_button() }}
</form>
myapp.py
from flask import Flask, make_response, render_template, session
from flask_restful import Resource, Api
from flask_wtf import csrf, Form
from wtforms import SubmitField
app = Flask(__name__)
app.secret_key = '5accdb11b2c10a78d7c92c5fa102ea77fcd50c2058b00f6e'
api = Api(app)
num_elements_to_generate = 500
class HelloForm(Form):
submit_button = SubmitField('Submit This Form')
class Hello(Resource):
def check_session(self):
if session.get('big'):
message = "session['big'] contains {} elements<br>".format(len(session['big']))
else:
message = "There is no session['big'] set<br>"
message += "session['secret'] is {}<br>".format(session.get('secret'))
message += "session['csrf_token'] is {}<br>".format(session.get('csrf_token'))
return message
def get(self):
myform = HelloForm()
session['big'] = list(range(num_elements_to_generate))
session['secret'] = "A secret phrase!"
csrf.generate_csrf()
message = self.check_session()
return make_response(render_template("hello.html", message=message, form=myform), 200, {'Content-Type': 'text/html'})
def post(self):
csrf.generate_csrf()
message = self.check_session()
return make_response("<p>This is the POST result page</p>" + message, 200, {'Content-Type': 'text/html'})
api.add_resource(Hello, '/')
if __name__ == '__main__':
app.run(debug=True)
在将num_elements_to_generate设置为500的情况下运行此命令,您将获得以下内容:
session['big'] contains 500 elements
session['secret'] is 'A secret phrase!'
session['csrf_token'] is a6acb57eb6e62876a9b1e808aa1302d40b44b945
和“提交此表单”按钮.单击按钮,您将获得:
This is the POST result page
session['big'] contains 500 elements
session['secret'] is 'A secret phrase!'
session['csrf_token'] is a6acb57eb6e62876a9b1e808aa1302d40b44b945
一切都很好.但是现在将num_elements_to_generate更改为3000,清除cookie,重新运行应用程序并访问页面.您会得到类似的信息:
session['big'] contains 3000 elements
session['secret'] is 'A secret phrase!'
session['csrf_token'] is 709b239857fd68a4649deb864868897f0dc0a8fd
和“提交此表单”按钮.单击按钮,这一次您将获得:
This is the POST result page
There is no session['big'] set
session['secret'] is 'None'
session['csrf_token'] is 13553dce0fbe938cc958a3653b85f98722525465
会话变量中存储的3000位数字过多,因此会话变量不会在请求之间持久存在.有趣的是,它们确实存在于第一页的会话中(无论您生成多少元素),但它们将无法生存到下一个请求. Flask-WTF会在发布表单时在会话中看不到csrf_token,因此会生成一个新的.如果这是表单验证步骤,则CSRF验证将失败.
这似乎是一个已知的Flask(或Werkzeug)错误with a pull request here.我不确定Flask为什么不在此处生成警告-除非从技术上讲是不可行的,否则它会默默地保持失败,这是一个意想不到的不愉快的惊喜Cookie太大时的会话变量.