2014-06-26 29 views
2

我有一个CSRF保护的表单在开发环境中完美运行(Flask运行服务器本身的app.run),但在Apache中通过mod_wsgi运行应用程序时失败。我使用的版本是:由于会话重置导致的Apache Flask中的CSRF令牌不匹配

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的,可能导致会议不能请求之间仍然存在?

+0

你的mod_wsgi配置是什么样的? –

+0

@SeanVieira我加了'httpd.conf'的一部分。我只加载'mod_wsgi'模块('LoadModule wsgi_module lib/httpd/mod_wsgi.so'),但没有改变它的配置。 – Midnighter

+0

我的猜测是你每次处理两个不同的进程,因为当你的代码被加载时,你使用'os.urandom'创建你的密钥,这会导致每个进程都有一个* * *密码,这是不你想要什么。要确认,请尝试使用硬编码的密钥,并查看是否解决了问题。如果是这样,您可以在部署时生成一个密钥,并将其存储在磁盘或内存中,并引用您的生产密钥。 [查看更多](https://code.google.com/p/modwsgi/wiki/ProcessesAndThreading#Sharing_Of_Global_Data) –

回答

0

这里会发生什么事是你存储使用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) 
+0

我没有一切设置立即测试它。不过,这听起来很合理。谢谢你的解释。 – Midnighter