【问题标题】:CherryPy sessions for same domain, different port相同域、不同端口的 CherryPy 会话
【发布时间】:2011-02-05 12:33:50
【问题描述】:

考虑下面的脚本。它将启动两个子进程,每个子进程都是 CherryPy 应用程序(按 Ctrl+C 或系统上的任何 KeyboardInterrupt 组合以结束它们)。如果您使用 CP 3.0 运行它(注意更改“StartServer”中的 3.0/3.1 特定行),请访问:

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 中。

您可以尝试各种方式:更改为文件存储、分离存储路径……唯一的区别是会话可能会被合并而不是被删除。我也读过another post 关于这个,并且有一个建议将会话工具配置键放在应用程序配置而不是全局配置中,但我认为这与应用程序独立运行的这种用法无关.

如何让独立的 CherryPy 应用程序不相互干扰?

注意:我最初是在 CherryPy mailing list 上提出这个问题的,但还没有得到答复,所以我在这里尝试。我希望没关系。

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)

【问题讨论】:

    标签: python session cherrypy


    【解决方案1】:

    存储会话标识符的 cookie 绑定到主机,而不是主机+端口。当您访问第一个站点时,您会在3.1(但不是3.0)中获得新的会话ID,然后您填写会话数据并可以看到它。之后,您使用此会话 ID 转到其他端口,但现在它无效(我相信您可以在调试模式下的日志中看到这一点)。因此服务器会向您发送新的会话 ID。现在您返回第一台服务器,您的标识符再次无效,因此您获得了新的。当然,这个新标识符的会话中没有数据。

    更新:RFC 2109,第 4.3.1 节解释 Set-Cookie 说:

    用户代理单独跟踪 通过到达的状态信息 来自每个的 Set-Cookie 响应标头 源服务器(区别于 名称或 IP 地址和端口)。

    但标准的解释并不那么明显。以下是 Firefox 跟踪器中相关 ticket 的引用:

    cookie 有两个 RFC,2109 (对于 set-cookie)和 2965(对于 设置cookie2)

    在 RFC 2109 的第 4.3.1 节中 解释它声明的 Set-Cookie
    “域默认为请求主机。 " 在第 2 节术语中 声明“请求主机和 request-URI 指的是值 客户端将发送到服务器, 分别是主机(但不是端口) 和 abs_path 部分 HTTP 的绝对URI (http_URL) 请求行。注意 请求主机必须是 FQHN。”在 RFC 第 3.3.1 节中的 2965 解释 Set-Cookie2 它声明“域 默认为有效 请求主机。 “它还说” 端口 默认行为是 饼干可能是 返回到任何请求端口。 " 在第 1 节中 术语它指出“术语 request-host 和 request-URI 指 客户的价值观 将分别作为主机(但不是端口)发送到服务器 和 HTTP 的 absoluteURI (http_URL) 的 abs_path 部分 请求行。 "(就像 RFC 2109)

    我对这些的解释是 端口号不应用于 记录 cookie 域,除非 set-cookie2 标头明确定义 端口号。

    【讨论】:

    • 那么...这是否意味着没有解决方案?在 CherryPy 中无法区分这两个站点?我认为,由于它在 3.0 中有效,但在 3.1 中无效,因此我可以更改一些东西(在 API 级别,而不是通过修改核心)。
    • 顺便说一句,我怀疑他们对基于名称的区分也是错误的,但我必须检查一下。
    • 根据上面的链接,如果不切换到具有端口参数的 Set-Cookie2 标头,没有可靠的方法可以使其在所有浏览器中工作。
    • 我得看看是否可以让 CherryPy tools.session 模块使用该标头。
    • 我已经更新了 CherryPy 3.0 和 3.1 行为差异的答案。虽然它应该被视为 3.0 中的错误,但您可以修改 3.1 的本地副本以将其取回。
    【解决方案2】:

    TL;DR:将 CherryPy 配置参数 tools.sessions.name 更改为每个应用程序独有的参数。

    长答案

    我知道这是一个非常古老的问题,但我认为有一个非常简单的答案。为了将来的搜索者的利益而写在下面。

    CherryPy 使用 cookie 来查找会话。默认情况下,这称为“session_id”,并有一个随机的十六进制字符串作为其值。如果给 CherryPy 一个它无法识别的 session_id,它会生成一个新的 session_id。这是一种防止会话固定的措施。

    当您在同一个域上有两个应用程序时。它们都使用相同的 cookie 名称(即“session_id”),但都不识别另一个的 session_id,因此他们用新的名称覆盖它。因此,从一个应用程序转移到另一个应用程序会使会话无效。

    解决方案很简单:在 CherryPy 配置中,您可以通过将 tools.sessions.name 设置为“session_id”以外的名称来覆盖 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-protection

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-30
      • 1970-01-01
      • 2015-06-18
      • 2013-10-12
      • 2011-01-15
      • 2023-03-23
      • 1970-01-01
      • 2014-08-06
      相关资源
      最近更新 更多