pythonformsflaskflask-wtforms

Form validation fails due missing CSRF


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

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

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 Get I retrieve an empty form.errors as expected. Now I fill out the form and submit it and form.errors is showing: {'csrf_token': [u'CSRF token missing']}

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

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

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 :

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.

return hmac_compare == hmac_csrf

I have both SECRET_KEY and CSRF_SESSION_KEY in my config defined.


Solution

  • The Flask-WTF CSRF infrastructure rejects a token if:

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

    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_KEY attribute set but is not stable across requests, or the app WTF_CSRF_SECRET_KEY key has changed (the latter defaults to the app.secret_key value).

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

    {{ form.hidden_tag() }}