Flask-WTF CSRF validation fails when app moved to

2019-03-01 05:19发布

I just set up my production environment for the Flask app I've been developing.

This stack is:

Windows Server 2012 R2 
Hyper-V VM 
    - Running Ubuntu 14.04
Docker 1.5 
    - Running Containers: 
        mysql 5.6
        nginx 1.6.3
        uwsgi 2.0.10

So basically I have a static IP set up in Ubuntu, port 80 getting forwarded to the nginx container, a data volume shared between the nginx and uwsgi containers for a socket file, and finally a link between the mysql container and the uwsgi container for database communication.

I've been using CSRF in the below implementation in my local development environment for awhile now with no issues:

from flask_wtf import Form

class UserForm(Form):
    ...

And then in my template:

<form>
    {{ form.hidden_tag() }}
    ...
</form>

However when I moved my app, unchanged, to the environment specified above, I get:

Bad Request

CSRF token missing or incorrect.

in response when I attempt to submit a form. I am using the same exact requirements.txt file with pip to setup my Flask app. I've also verified that the CSRF token is indeed in the HTML of the page.

Anyone have any clue as to why this is happening?

EDIT:

To answer the questions in the comments: The version of Flask-WTF and WTForms in both my production docker container and my development local virtualenv are the same. Here's my requirements file, spawned from my dev environment and used to set up my app inside the docker container:

Flask==0.10.1
Flask-Login==0.2.11
Flask-Mail==0.9.1
Flask-Migrate==1.2.0
Flask-MySQL==1.2
Flask-Principal==0.4.0
Flask-SQLAlchemy==2.0
Flask-Script==2.0.5
Flask-Security==1.7.4
Flask-WTF==0.10.2
Flask-WhooshAlchemy==0.55a
Jinja2==2.7.3
Mako==1.0.0
MarkupSafe==0.23
PyMySQL==0.6.2
SQLAlchemy==0.7.9
Tempita==0.5.2
WTForms==2.0.1
Werkzeug==0.9.6
Whoosh==2.6.0
alembic==0.6.7
blinker==1.3
chardet==2.2.1
decorator==3.4.0
itsdangerous==0.24
lamson==1.3.4
passlib==1.6.2
py-bcrypt==0.4
pytz==2014.9
sqlalchemy-migrate==0.7.2

That being said, there are a couple extra modules in my docker container due to installing uWSGI as well as my flask app. Here are the additional ones.

argparse (1.2.1)
colorama (0.2.5)
docutils (0.12)
html5lib (0.999)
lockfile (0.10.2)
mock (1.0.1)
MySQL-python (1.2.5)
nose (1.3.6)
python-daemon (2.0.5)
python-modargs (1.7)
requests (2.2.1)
six (1.5.2)
urllib3 (1.7.1)
uWSGI (2.0.10)
wheel (0.24.0)
wsgiref (0.1.2)

Wow, so more than a couple. Perhaps I have a conflicting package?

Regarding SECRET_KEY and CSRF_SESSION_KEY: I had SECRET_KEY defined in my config.py. I was lead to believe that without defining CSRF_SESSION_KEY that it would default to SECRET_KEY. Nevertheless, I went ahead and defined a CSRF_SESSION_KEY, but it didn't seem to make any difference.

Lastly, I looked into the API change from {{ form.hidden_tag() }} to {{ form.csrf_token }}. They still have references to both forms in the docs for the latest version. On the CSRF page, they have the latter, but in the quickstart they still have the former. I'm not sure what is up with this, but just to be certain I implemented the version on the CSRF page as follows:

init.py:

from flask_wtf.csrf import CsrfProtect

CsrfProtect(app)

user_login.html

<form>
    {{ form.csrf_token }}
    ...
</form>

But I still end up getting the same response.

As I said before, the Flask-WTF documentation is all over the place. So I've been trying all sorts of combinations. At the bottom of this page: http://flask-wtf.readthedocs.org/en/latest/csrf.html they reference an incorrect import, so I replaced my

from flask_wtf import Form

with

from flask.ext.wtf import Form

Still no dice. Thanks for the comments though, gave me more to try out!

I also just tried consolidating everything into a single docker container, but I still get the same result.

1条回答
时光不老,我们不散
2楼-- · 2019-03-01 05:38

Basically I had this in my development config.py file:

SERVER_NAME = 'localhost:5000'

When I put the app on the production environment, I had to change it to:

SERVER_NAME = '192.168.1.66'

for the app to show up at all. But in the end I had to comment it out altogether. From my research it's only necessary when you're putting the app on a subdomain, which I am not.

If anyone has any more insight into why this would cause CSRF token invalidation, I'd love to hear it.. I've been unable to track this down any further. So for now, I've removed it altogether.

查看更多
登录 后发表回答