由于会话重置,Apache Flask中的CSRF令牌不匹配

时间:2014-06-26 09:45:52

标签: apache python-2.7 flask csrf flask-wtforms

我有一个CSRF保护表单的示例,它在开发环境中完美运行(Flask使用app.run运行服务器本身)但是当我在Apache中通过mod_wsgi运行应用程序时失败。我使用的版本是:

Server version: Apache/2.4.4 (Unix)
Python 2.7.3
Flask==0.10.1
Flask-WTF==0.9.5
WTForms==2.0
Flask-KVSession==0.4
simplekv==0.8.4

失败的原因是表单验证期间csrf_token不匹配。我在视图的开头记录flask.sessionflask.request.form的内容,并在视图的末尾再次记录会话的内容。在开发模式下,会话中csrf_token的内容在多个请求中保持不变,例如

<KVSession {'csrf_token': '79918c1e3191e4d4fe89a9499f576404a18be8e4'}>

在两种情况下都能正确传输表格的内容,例如,

ImmutableMultiDict([('csrf_token', u'1403778775.86##34f1447f1b8c78808f4e71f2ff037bcd1df41dcd'),
('time', u'8'), ('submit', u'Go'), ('dose', u'Low')])

当我通过Apache运行我的应用程序时,会话内容会随每个请求重置。在视图的开头,会话内容为空:

<KVSession {}>

然后每次设置一个新的令牌,导致不匹配。目前,我的__init__.py模块如下所示:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from simplekv.memory import DictStore
from flaskext.kvsession import KVSessionExtension


app = Flask(__name__)
app.config.from_object("myapp.config.Config")

db = SQLAlchemy(app)

store = DictStore()
KVSessionExtension(store, app)

from . import views

我删除了KVSession语句,并没有改变问题。所以我认为服务器端会话不是罪魁祸首。

是的,我已在配置中将SECRET_KEY设置为os.urandom(128)

httpd.conf的相关(我认为)部分是:

Listen url.com:8090
<VirtualHost url.com:8090>

    # --- Configure VirtualHost ---

    LogLevel debug

    ServerName url.com

    DocumentRoot /path/to/flaskapp/htdocs

    <Directory />
        Options FollowSymLinks
        AllowOverride None
    </Directory>

    <Directory /path/to/flaskapp/htdocs/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Require all granted
    </Directory>

    # --- Configure WSGI Listening App(s) ---

    WSGIDaemonProcess mysite user=me group=us processes=2 threads=10
    WSGIScriptAlias / /path/to/flaskapp/wsgi/wsgi.py

    <Directory /path/to/flaskapp/wsgi/>
        WSGIProcessGroup mysite
        WSGIApplicationGroup %{GLOBAL}
        WSGIScriptReloading On
        Require all granted
    </Directory>

    # --- Configure Static Files ---

    Alias /static/ /path/to/flaskapp/htdocs/static/
    Alias /tmp/ /path/to/flaskapp/htdocs/tmp/

</VirtualHost>

有没有人知道Apache设置或mod_wsgi与Flask交互可能会导致会话在请求之间不会持续存在?

1 个答案:

答案 0 :(得分:1)

这里发生的是您使用Flask-KVSession存储会话,并提供基于内存的DictStore作为存储:

from simplekv.memory import DictStore

store = DictStore()
KVSessionExtension(store, app)

根本原因

在单线程环境中,这将起作用。但是,当多个进程发挥作用时,它们不共享相同的内存,并且会创建多个DictStore实例,每个进程一个。因此,当两个后续请求由两个不同的进程提供时,第一个请求将无法将会话更改传递给下一个请求。

或者更短:两个进程=两个CSRF令牌。不好。

<强>解决方案

使用永久存储。这就是我使用的:

def configure_session(app):
    with app.app_context():
        if config['other']['local_debug']:
            store = simplekv.memory.DictStore()
        else:
            store = simplekv.db.sql.SQLAlchemyStore(engine, metadata, 'sessions')

        # Attach session store
        flask_kvsession.KVSessionExtension(store, app)