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

时间:2014-02-01 17:17:00

标签: python forms flask flask-wtforms

几天前,我已经重置了我的本地烧瓶环境,而没有在删除它之前通过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。

4 个答案:

答案 0 :(得分:19)

Flask-WTF CSRF基础设施在以下情况下拒绝令牌:

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

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

  • 如果在当前会话中找不到'csrf_token'密钥。您显然可以看到会话令牌,因此也是如此。

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

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

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

版本0.9.0中添加了CSRF支持,如果升级,请查看特定的CSRF protection documentation。标准的Flask-WTF Form CSRF令牌包含为隐藏字段,使隐藏字段足以包含它:

{{ form.hidden_tag() }}

答案 1 :(得分:16)

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

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

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

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

documentation现在显然反映了这一点,但我不知道这已经发生变化并且在追逐鬼魂。

在不以某种方式通知开发人员的情况下,已弃用的功能是一个大问题。任何现在升级到最新版本的人都会像我一样追逐鬼魂。但它也是我的错,没有拍摄我的依赖关系的快照。课程学到了很多东西。

答案 2 :(得分:0)

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

如果您的Flask服务器未在本地主机上运行,​​则为了使Flask正常运行,您需要设置一个SERVER_NAME环境变量。您可能忘记了在某个地方修改SERVER_NAME值。

例如,您可以在config/settings.py中输入以下内容:

SERVER_NAME = 'my-domain.com'

有关更多信息,请查看this great resource

答案 3 :(得分:0)

在创建应用程序时:

from flask_wtf.csrf import CsrfProtect

csrf = CsrfProtect()

app = Flask(__name__)   

...

csrf.init_app(app)

...