#apache #python-2.7 #flask #csrf #flask-wtforms
#apache #python-2.7 #flask #csrf #flask-wtforms
Вопрос:
У меня есть пример защищенной формы CSRF, которая отлично работает в среде разработки (Flask запускает сам сервер app.run
), но терпит неудачу при запуске приложения mod_wsgi
через Apache. Версии, которые я использую:
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, которые могут привести к тому, что сеанс не будет сохраняться между запросами?
Комментарии:
1. Как выглядит ваша конфигурация mod_wsgi?
2. @SeanVieira Я добавил часть
httpd.conf
. Я только загружаюmod_wsgi
модуль (LoadModule wsgi_module lib/httpd/mod_wsgi.so
), но не менял его конфигурацию.3. Я предполагаю , что вы каждый раз обрабатываетесь двумя разными процессами, и поскольку вы используете
os.urandom
для создания своего секретного ключа при загрузке вашего кода, это приводит к тому, что каждый процесс имеет свой секрет, а это не то, что вы хотите. Для подтверждения попробуйте жестко запрограммированный секретный ключ и посмотрите, устраняет ли это проблему. Если это произойдет, вы можете сгенерировать секретный ключ во время развертывания и сохранить его либо на диске, либо в памяти и ссылаться на него для вашего производственного секретного ключа. Подробнее смотрите здесь4. @SeanVieira вы, по крайней мере, к чему- то пришли. Фиксированный ключ не решил мои проблемы, сеанс все еще был пуст в начале запроса. Уменьшение количества процессов в конфигурации
1
решило проблему. Я продолжу изучать документацию, спасибо за эту ссылку.
Ответ №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)
Комментарии:
1. У меня не все настроено для немедленного тестирования. Тем не менее, это звучит разумно. Спасибо за хорошее объяснение.