CherryPy sessions for same domain, different port

匿名 (未验证) 提交于 2019-12-03 01:22:02

问题:

Consider the script below. It will launch two subprocesses, each one a CherryPy app (hit Ctrl+C or whatever the KeyboardInterrupt combo is on your system to end them both). If you run it with CP 3.0 (taking care to change the 3.0/3.1 specific lines in "StartServer"), then visit:

http://localhost:15002/

...you see an empty dict. Then visit:

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

http://localhost:15002/

...and you see the newly populated dict. Then visit:

http://localhost:15012/

...and go back to

http://localhost:15002/

...and nothing has changed.

If you try the same thing with CP 3.1 (remember the lines in "StartServer"!), when you get to the last step, the dict is now empty. This happens in Windows and Debian, Python 2.5 and 2.6.

You can try all sorts of things: changing to file storage, separating the storage paths... the only difference it makes is that the sessions might get merged instead of erased. I've read another post about this as well, and there's a suggestion there to put the session tools config keys in the app config rather than the global config, but I don't think that's relevant to this usage where the apps run independently.

What do I do to get independent CherryPy applications to NOT interfere with each other?

Note: I originally asked this on the CherryPy mailing list but haven't had a response yet so I'm trying here. I hope that's okay.

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) 

回答1:

The cookie, where session identifier is stored, is bound to host, not host+port. When you visit the first site you get new session id in 3.1 (but not in 3.0), then you fill session data and can see it. After that you go to other port with this session id, but now it's invalid (I believe you can see this in log in debugging mode). So the server send you new session id. Now you return to first server and again your identifier is invalid so you get new one. Sure, there is no data in the session in for this new identifier.

Update: RFC 2109, section 4.3.1 Interpreting Set-Cookie says:

The user agent keeps separate track of state information that arrives via Set-Cookie response headers from each origin server (as distinguished by name or IP address and port).

But interpretation of standard is not so obvious. Here is a cite from the related ticket in firefox tracker:

There are two RFC for cookies, 2109 (For set-cookie) and 2965 (For set-cookie2)

In RFC 2109 in section 4.3.1 Interpreting Set-Cookie it states
"Domain Defaults to the request-host. " And in section 2 TERMINOLOGY it states "The terms request-host and request-URI refer to the values the client would send to the server as, respectively, the host (but not port) and abs_path portions of the absoluteURI (http_URL) of the HTTP request line. Note that request-host must be a FQHN." In RFC 2965 in section 3.3.1 Interpreting Set-Cookie2 it states "Domain Defaults to the effective request-host. " it also states " Port The default behavior is that a cookie MAY be returned to any request-port. " And in section 1 TERMINOLOGY it states " The terms request-host and request-URI refer to the values the client would send to the server as, respectively, the host (but not port) and abs_path portions of the absoluteURI (http_URL) of the HTTP request line. " (Just like RFC 2109)

My interpretation of these is that port numbers should not be used for recording cookie domains unless a set-cookie2 header explicitly defines port number.



回答2:

TL;DR: Change the CherryPy config parameter tools.sessions.name to something unique to each application.

Long answer:

I know this is a very old question, but I think there is a very simple answer. Writing below for the benefit of future searchers.

CherryPy uses a cookie to find sessions. By default, this is called "session_id" and has a random hexadecimal string as its value. If CherryPy is given a session_id that it doesn't recognise, it generates a new session_id. This is a measure to prevent session fixation.

When you have two applications on the same domain. They both use the same cookie name (i.e. "session_id"), but neither recognises the session_id of the other and so they overwrite it with a new one. So moving from one application to the other invalidates the session.

The solution is simple: In the CherryPy configuration, you can override the session_id name by setting tools.sessions.name to something other than "session_id", for example "myapp_session_id" and "myotherapp_session_id".

You need to make sure the session storage is separate as you've correctly identified.

From the example above, you'd do something like this:

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     } } 

Note: In my own apps using CherryPy 10.0.0, I've used this config option at the app level and at a path level. I haven't tested this with old versions of CherryPy, but looking at the source code, it looks like it's been possible for over a decade.

Since writing this, I've contributed an update to CherryPy's documentation about this, included here: http://docs.cherrypy.org/en/latest/pkg/cherrypy.lib.html#session-fixation-protection



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!