我有一个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.session
和flask.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交互可能会导致会话在请求之间不会持续存在?
答案 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)