Сеансы CherryPy для одного домена, другой порт

Рассмотрим сценарий ниже. Он запустит два подпроцесса, каждый из которых представляет собой приложение CherryPy (нажмите Ctrl+C или любое другое сочетание KeyboardInterrupt в вашей системе, чтобы завершить их оба). Если вы запускаете его с CP 3.0 (заботясь об изменении конкретных строк 3.0/3.1 в «StartServer»), посетите:

http://localhost:15002/

... вы видите пустой диктофон. Затем посетите:

http://localhost:15002/set?val=10

http://localhost:15002/

... и вы видите недавно заполненный словарь. Затем посетите:

http://localhost:15012/

...и вернуться к

http://localhost:15002/

... и ничего не изменилось.

Если вы попробуете то же самое с CP 3.1 (помните строки в «StartServer»!), когда вы дойдете до последнего шага, dict теперь будет пуст. Это происходит в Windows и Debian, Python 2.5 и 2.6.

Вы можете попробовать все что угодно: перейти на файловое хранилище, разделить пути к хранилищу... единственная разница в том, что сеансы могут быть объединены, а не стерты. Я прочитал еще один пост об этом. , и есть предложение поместить ключи конфигурации инструментов сеанса в конфигурацию приложения, а не в глобальную конфигурацию, но я не думаю, что это имеет отношение к этому использованию, когда приложения работают независимо.

Что мне сделать, чтобы независимые приложения CherryPy НЕ мешали друг другу?

Примечание. Сначала я задал этот вопрос в списке рассылки CherryPy, но еще не было ответа, поэтому я пытаюсь здесь. Я надеюсь, что все в порядке.

import os, os.path, socket, sys
import subprocess
import cgi

import cherrypy

HTTP_PORT = 15002
HTTP_HOST = "127.0.0.1"

site1conf = {
    'global' : {
        'server.socket_host' : HTTP_HOST,
        'server.socket_port' : HTTP_PORT,
        'tools.sessions.on' : True,
#        'tools.sessions.storage_type': 'file',
#        'tools.sessions.storage_path': '1',
#        'tools.sessions.storage_path': '.',
        'tools.sessions.timeout' : 1440}}

site2conf = {
    'global' : {
        'server.socket_host' : HTTP_HOST,
        'server.socket_port' : HTTP_PORT + 10,
        'tools.sessions.on' : True,
#        'tools.sessions.storage_type': 'file',
#        'tools.sessions.storage_path': '2',
#        'tools.sessions.storage_path': '.',
        'tools.sessions.timeout' : 1440}}


class Home(object) :

    def __init__(self, key):
        self.key = key

    @cherrypy.expose
    def index(self):
        return """\
<html>
<body>Session:
<br>%s
</body>
</html> """ % cgi.escape(str(dict(cherrypy.session)))

    @cherrypy.expose
    def set(self, val):
        cherrypy.session[self.key.upper()] = val
        return """\
<html>
<body>Set %s to %s</body>
</html>""" % (cgi.escape(self.key), cgi.escape(val))

def StartServer(conf, key):
    cherrypy.config.update(conf)

    print 'Starting server (%s)' % key
    cherrypy.tree.mount(Home(key), '/', {})

    # Start the web server.
    #### 3.0
    # cherrypy.server.quickstart()
    # cherrypy.engine.start()
    ####

    #### 3.1
    cherrypy.engine.start()
    cherrypy.engine.block()
    ####

def Main():
    # Start first webserver
    proc1 = subprocess.Popen(
        [sys.executable, os.path.abspath(__file__), "1"])
    proc2 = subprocess.Popen(
        [sys.executable, os.path.abspath(__file__), "2"])

    proc1.wait()
    proc2.wait()

if __name__ == "__main__":

    print sys.argv

    if len(sys.argv) == 1:
        # Master process
        Main()
    elif(int(sys.argv[1]) == 1):
        StartServer(site1conf, 'magic')
    elif(int(sys.argv[1]) == 2):
        StartServer(site2conf, 'science')
    else:
        sys.exit(1)

person detly    schedule 08.04.2010    source источник


Ответы (2)


Файл cookie, в котором хранится идентификатор сеанса, привязан к хосту, а не к хосту+порту. Когда вы посещаете первый сайт, вы получаете новый идентификатор сеанса в 3.1 (но не в 3.0), затем вы заполняете данные сеанса и можете их видеть. После этого вы переходите на другой порт с этим идентификатором сеанса, но теперь он недействителен (я думаю, вы можете увидеть это в журнале в режиме отладки). Таким образом, сервер отправляет вам новый идентификатор сеанса. Теперь вы возвращаетесь на первый сервер, и снова ваш идентификатор недействителен, поэтому вы получаете новый. Конечно, в сеансе нет данных для этого нового идентификатора.

Обновление: RFC 2109, раздел 4.3.1 «Интерпретация Set-Cookie» гласит:

Пользовательский агент отдельно отслеживает информацию о состоянии, которая поступает через заголовки ответов Set-Cookie от каждого исходного сервера (в зависимости от имени или IP-адреса и порта).

Но интерпретация стандарта не столь очевидна. Вот цитата из соответствующей тикета в трекере Firefox:

Существует два RFC для файлов cookie: 2109 (для set-cookie) и 2965 (для set-cookie2).

В RFC 2109 в разделе 4.3.1 Интерпретация Set-Cookie указано
«Домен по умолчанию — это хост запроса». А в разделе 2 ТЕРМИНОЛОГИЯ указано: «Термины запрос-хост и запрос-URI относятся к значениям будет отправлять на сервер как, соответственно, части host (но не port) и abs_path абсолютного URI (http_URL) строки HTTP-запроса. Обратите внимание, что request-host должен быть FQHN». В RFC 2965 в разделе 3.3.1 «Интерпретация Set-Cookie2» указано «Домен по умолчанию соответствует действующему хосту запроса». Также указано «Порт. Поведение по умолчанию заключается в том, что файл cookie МОЖЕТ быть возвращен на любой порт запроса». И в разделе 1 ТЕРМИНОЛОГИЯ в нем говорится: «Термины request-host и request-URI относятся к значениям, которые клиент будет отправлять на сервер, соответственно, в части host (но не port) и abs_path абсолютного URI (http_URL) строки HTTP-запроса. » (как в RFC 2109)

Моя интерпретация этого заключается в том, что номера портов не должны использоваться для записи доменов cookie, если заголовок set-cookie2 явно не определяет номер порта.

person Denis Otkidach    schedule 12.04.2010
comment
Итак... значит ли это, что решения нет? Невозможно различить два сайта в CherryPy? Я подумал, что раз это работает в 3.0, но не в 3.1, что-то изменилось, что я мог бы изменить обратно (на уровне API, а не путем взлома ядра). - person detly; 12.04.2010
comment
Кстати, я подозреваю, что они ошибаются и в том, что касается дифференциации по имени, но мне нужно это проверить. - person detly; 12.04.2010
comment
По приведенным выше ссылкам нет надежного способа заставить его работать во всех браузерах без переключения на заголовок Set-Cookie2 с параметром Port. - person Denis Otkidach; 12.04.2010
comment
Мне нужно посмотреть, можно ли заставить модуль CherryPy tools.session использовать этот заголовок. - person detly; 13.04.2010
comment
Я обновил ответ, указав разницу в поведении CherryPy 3.0 и 3.1. Хотя это следует рассматривать как ошибку в версии 3.0, вы можете изменить локальную копию версии 3.1, чтобы вернуть ее. - person Denis Otkidach; 13.04.2010
comment
Хм, я не вижу нигде кроме тестов, что используется Set-Cookie. Где-то происходит какая-то странная магия. Замечательный. - person detly; 13.04.2010
comment
Set-Cookie используется в стандартном модуле Cookie. - person Denis Otkidach; 13.04.2010
comment
Это может дать мне достаточно информации, чтобы разобраться во всем, и даже если это не так, я ценю объяснение :) - person detly; 14.04.2010

TL;DR: измените параметр конфигурации CherryPy tools.sessions.name на уникальный для каждого приложения.

Длинный ответ:

Я знаю, что это очень старый вопрос, но я думаю, что есть очень простой ответ. Пишу ниже для будущих искателей.

CherryPy использует файл cookie для поиска сеансов. По умолчанию это называется «session_id» и имеет случайную шестнадцатеричную строку в качестве значения. Если CherryPy получает идентификатор session_id, который он не распознает, он создает новый идентификатор session_id. Это мера предотвращения фиксации сеанса.

Когда у вас есть два приложения в одном домене. Оба они используют одно и то же имя файла cookie (например, «session_id»), но ни один из них не распознает session_id другого и поэтому перезаписывает его новым. Таким образом, переход от одного приложения к другому делает сеанс недействительным.

Решение простое: в конфигурации CherryPy вы можете переопределить имя session_id, задав tools.sessions.name значение, отличное от "session_id", например "myapp_session_id" и "myotherapp_session_id".

Вы должны убедиться, что хранилище сеансов отделено, как вы правильно определили.

В приведенном выше примере вы должны сделать что-то вроде этого:

site1conf = {
    'global': {
        'server.socket_host': HTTP_HOST,
        'server.socket_port': HTTP_PORT,
        'tools.sessions.on': True,
        'tools.sessions.storage_type': 'file',
        'tools.sessions.storage_path': '/tmp/site1_sessions/',
        'tools.sessions.name': 'site1_session_id',
        'tools.sessions.timeout': 1440
    }
}
site2conf = {
    'global': {
        'server.socket_host': HTTP_HOST,
        'server.socket_port': HTTP_PORT + 10,
        'tools.sessions.on': True,
        'tools.sessions.storage_type': 'file',
        'tools.sessions.storage_path': '/tmp/site2_sessions/',
        'tools.sessions.name': 'site2_session_id',
        'tools.sessions.timeout': 1440
    }
}

Примечание. В своих собственных приложениях, использующих CherryPy 10.0.0, я использовал этот параметр конфигурации на уровне приложения и на уровне пути. Я не тестировал это со старыми версиями CherryPy, но, глядя на исходный код, похоже, что это было возможно уже более десяти лет.

После написания этой статьи я внес обновление в документацию CherryPy по этому поводу, включенное здесь: http://docs.cherrypy.org/en/latest/pkg/cherrypy.lib.html#session-fixation-защита

person Michael Mulqueen    schedule 12.07.2017