flask-wtf icon indicating copy to clipboard operation
flask-wtf copied to clipboard

CSRF Tokens do not match

Open stevesmit opened this issue 5 years ago • 7 comments

Hello, I am extending the FlaskForm object in my form.

The form doesn't successfully validate, here are the commands and their output. These commands are run within the route:

print(request.form)
print(form.is_submitted())
print(form.validate())
print(form.errors)

ImmutableMultiDict([('csrf_token', 'ImFlNzJlMTQ4OGY1Y2EwNGY0ODY5OWViMWUzMjA5MjdiZmNlODVjODIi.X8gTcw.CfkkIkePAuHSe_EvU_fzxrUhwXw'), ('accept', 'y'), ('submit', 'Submit')])
True
False
{'csrf_token': ['The CSRF tokens do not match.']}

In the template for the page the form is on, I am including the {{ form.hidden_tag() }} line at the top of the form. I can't work out why it is doing this.

Strangely, when I run the flask app in development mode, this is not a problem and the form validates successfully (I confirmed this with the commands run above).

stevesmit avatar Dec 02 '20 22:12 stevesmit

I'm having the exact same issue! In my local development environment, this problem does not occur. It only occurs once my app is deployed on a server. I've deployed 3 apps so far into production servers (paid servers from Azure and PythonAnywhere) and this issue ALWAYS occurs once deployed.

Here are some things that I already checked.

  • I have included CSRF in the form, which I can see in the HTML.
<form method="POST">
    {{ form.hidden_tag() }}
</form>
  • I have set a SECRET_KEY in the configuration.
  • I have set the SERVER_NAME in the configuration.
  • I have set SESSION_COOKIE_SECURE to be False.
  • I am using Flask-Login Login Manager and I'm storing small strings (user_id) in the session.
  • Neither FireFox or Chome is blocking the “session” cookie and I can verify it is there in both browsers.
  • I can see the CSRF token come through in the POST request in the server.
  • This issue occurs regardless whether I use CSRFProtect() or not. When I do use CSRFProtect, I am NOT excluding any views from CSRF.
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)

Looking at this Stack Overflow post, a score of others are also experiencing the same issue: https://stackoverflow.com/questions/54027777/flask-wtf-csrf-session-token-missing-secret-key-not-found

I do believe that this is bug.

feelosophy13 avatar Dec 23 '20 02:12 feelosophy13

I can confirm that I am encountering this issue too. However I am not able to build a snippet for reproducing the bug every time.

If I am filling a faulty form in a private tab, the issue does not appear. Can you confirm that?

What wtforms version are you using?

edit: My website answers to two domains, strangely one is encountering this issue, and the other is not. On the failing domain, there are two session cookies, one which domain is .domain1.tld and another one which domain is domain1.tld, without the starting dot. On the succeeding domain, there is only one session cookie, which domain is domain2.tld, without the starting dot.

edit2: In the failing domain, I can see that every form submission passes through this line. It feels like session['csrf_token'] is never written, or deleted somewhere else, or written in th.

https://github.com/lepture/flask-wtf/blob/c0e42ac78c347d0d26496a8903b854f24f1b3107/flask_wtf/csrf.py#L46-L47

edit3: This issue may be related. It seems this issue is encountered when several session cookies are involved.

edit4: The requests set-cookie header content matches the session id of the cookie whose domain is domain1.tld but the response Cookie header matches has both session ids, the first one being the session id of the cookie whose domain is .domain1.tld and the second one being the session id of the cookie whose domain is domain.tld.

azmeuk avatar Dec 29 '20 09:12 azmeuk

I'm having the same issue where it doesn't occur in my local environment, but happens once I deploy to AWS. Strangely I get the error CSRF token has expired when I use {{ form.hidden_tag() }} to render the CSRF token, but I get your CSRF tokens do not match when I use {{ form.csrf_token }}. At least I think that's what's happening, I can double check.

It always succeeds the second time I submit the form. I can confirm it does occur in private tabs as well.

k-time avatar Mar 20 '21 22:03 k-time

I'm also having this issue flask-wtf v1.0.1, but my deploy is in docker, using gunicorn and nginx reverse proxy. All pages are HTTPS. If I log in on my own, I have no issues, but if someone else tries to log into the site, then the other person will get CSRF Tokens do not match. I've also gotten The CSRF token has expired. so I assume they're related. Has anyone figured out what is causing this issue?

Some of my config.. Gunicorn

bind = "0.0.0.0:5005"
workers = 1
accesslog = "-"
loglevel = "debug"
capture_output = True
enable_stdio_inheritance = True

Settings

    SESSION_COOKIE_HTTPONLY = True
    REMEMBER_COOKIE_HTTPONLY = True
    REMEMBER_COOKIE_DURATION = 3600
csrf = CSRFProtect()

db.init_app(app)
login_manager.init_app(app)
csrf.init_app(app)
ldap_manager.init_app(app)

eljeffeg avatar Jul 20 '22 00:07 eljeffeg

@eljeffeg This issue went away for me, I can't remember how... it might've been updating to the latest version but I'm not sure

k-time avatar Jul 20 '22 01:07 k-time

~~I put together this example which seems to exhibit the issues I'm seeing. Maybe if we can figure that out, it might provide insight or solve these. https://github.com/eljeffeg/testCSRF~~

~~It can be run via docker or via flask run~~

Update: My example had issues and did not exhibit the issue.

eljeffeg avatar Jul 28 '22 20:07 eljeffeg

I believe I resolved the issue I was having, which was I had to add threads to the gunicorn config.

  • https://stackoverflow.com/a/72126425/666774
  • https://github.com/miguelgrinberg/flasky/issues/1

eljeffeg avatar Jul 31 '22 21:07 eljeffeg

Strangely, when I run the flask app in development mode, this is not a problem and the form validates successfully (I confirmed this with the commands run above).

This difference of behavior makes me think of a situation I just met.

If the cache policy (with the expires header for instance) of your webserver is longer than the CSRF token lifetime, then when your browser loads your form a second time, it will display a cached page with a possibly expired CSRF token.

This generally don't happen in local development, as development servers do not cache pages. I will update the documentation to mention this.

I'll close this issue for now, but if you still encounter this situation and have checked that your webserver cache policy is not the issue, we can reopen it.

azmeuk avatar Feb 23 '24 16:02 azmeuk