Несоответствие токена CSRF в Apache Flask из-за сброса сеанса

#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. У меня не все настроено для немедленного тестирования. Тем не менее, это звучит разумно. Спасибо за хорошее объяснение.