[Checkins] SVN: zope.session/trunk/ Added a 'secure' option on CookieClientIdManagers to cause the secure

Jim Fulton jim at zope.com
Fri Sep 12 11:47:17 EDT 2008


Log message for revision 91092:
  Added a 'secure' option on CookieClientIdManagers to cause the secure
  set-cookie option to be used, which tells the browser not to send the
  cookie over http.
  
  Only set the client-id cookie if it isn't already set and try to
  prevent the header from being cached.  This is to minimize risk from
  broken caches handing the same client id out to multiple users.
  

Changed:
  U   zope.session/trunk/CHANGES.txt
  U   zope.session/trunk/buildout.cfg
  U   zope.session/trunk/src/zope/session/http.py

-=-
Modified: zope.session/trunk/CHANGES.txt
===================================================================
--- zope.session/trunk/CHANGES.txt	2008-09-12 15:26:16 UTC (rev 91091)
+++ zope.session/trunk/CHANGES.txt	2008-09-12 15:47:17 UTC (rev 91092)
@@ -2,6 +2,21 @@
 CHANGES
 =======
 
+version 3.6.0 (2008-08-12)
+--------------------------
+
+New features:
+
+- Added a 'secure' option on CookieClientIdManagers to cause the secure
+  set-cookie option to be used, which tells the browser not to send the
+  cookie over http.
+
+  This provides enhanced security for ssl-only applications.
+
+- Only set the client-id cookie if it isn't already set and try to
+  prevent the header from being cached.  This is to minimize risk from
+  broken caches handing the same client id out to multiple users. 
+
 version 3.5.2 (2008-06-12)
 --------------------------
 

Modified: zope.session/trunk/buildout.cfg
===================================================================
--- zope.session/trunk/buildout.cfg	2008-09-12 15:26:16 UTC (rev 91091)
+++ zope.session/trunk/buildout.cfg	2008-09-12 15:47:17 UTC (rev 91092)
@@ -1,8 +1,12 @@
 [buildout]
 develop = . 
-parts = test
-find-links = http://download.zope.org/distribution/
+parts = test py
 
 [test]
 recipe = zc.recipe.testrunner
 eggs = zope.session [test]
+
+[py]
+recipe = zc.recipe.egg
+eggs = zope.session
+interpreter = py

Modified: zope.session/trunk/src/zope/session/http.py
===================================================================
--- zope.session/trunk/src/zope/session/http.py	2008-09-12 15:26:16 UTC (rev 91091)
+++ zope.session/trunk/src/zope/session/http.py	2008-09-12 15:47:17 UTC (rev 91092)
@@ -92,6 +92,11 @@
             default=False,
             )
 
+    secure = schema.Bool(
+        title=_('Request Secure communication'),
+        required=False,
+        default=False,
+        )
 
 class CookieClientIdManager(zope.location.Location, Persistent):
     """Session utility implemented using cookies."""
@@ -100,6 +105,7 @@
 
     thirdparty = FieldProperty(ICookieClientIdManager['thirdparty'])
     cookieLifetime = FieldProperty(ICookieClientIdManager['cookieLifetime'])
+    secure = FieldProperty(ICookieClientIdManager['secure'])
 
     def __init__(self):
         self.namespace = "zope3_cs_%x" % (int(time.time()) - 1000000000)
@@ -136,6 +142,23 @@
           >>> type(id) == type('')
           True
 
+        We don't set the client id unless we need to, so, for example,
+        the second response doesn't have cookies set:
+
+          >>> request2.response._cookies
+          {}
+
+        An exception to this is if the cookieLifetime is set to a
+        non-zero integer value, in which case we do set it on every
+        request, regardless of when it was last set:
+        
+          >>> bim.cookieLifetime = 3600 # one hour
+          >>> id == bim.getClientId(request2)
+          True
+
+          >>> bool(request2.response._cookies)
+          True
+
         It's also possible to use third-party cookies. E.g. Apache `mod_uid`
         or Nginx `ngx_http_userid_module` are able to issue user tracking
         cookies in front of Zope. In case thirdparty is activated Zope may
@@ -158,8 +181,10 @@
                 raise MissingClientIdException
             else:
                 sid = self.generateUniqueId()
-
-        if not self.thirdparty:
+                self.setRequestId(request, sid)
+        elif (not self.thirdparty) and self.cookieLifetime:
+            # If we have a finite cookie lifetime, then set the cookie
+            # on each request to avoid losing it.
             self.setRequestId(request, sid)
 
         return sid
@@ -242,9 +267,12 @@
         if self.thirdparty:
             return sid
         else:
-            # If there is an id set on the response, use that but don't trust it.
-            # We need to check the response in case there has already been a new
-            # session created during the course of this request.
+            
+            # If there is an id set on the response, use that but
+            # don't trust it.  We need to check the response in case
+            # there has already been a new session created during the
+            # course of this request.
+
             if sid is None or len(sid) != 54:
                 return None
             s, mac = sid[:27], sid[27:]
@@ -261,7 +289,7 @@
 
         See the examples in getRequestId.
 
-        Note that the id is checkec for validity. Setting an
+        Note that the id is checked for validity. Setting an
         invalid value is silently ignored:
 
             >>> from zope.publisher.http import HTTPRequest
@@ -277,9 +305,6 @@
             >>> cookie['path'] == request.getApplicationURL(path_only=True)
             True
 
-        In the future, it should be the site containing the
-        CookieClientIdManager
-
         By default, session cookies don't expire:
 
             >>> cookie.has_key('expires')
@@ -313,6 +338,29 @@
           >>> bim.setRequestId(request, '1234')
           >>> cookie = request.response.getCookie(bim.namespace)
           >>> cookie
+
+        If the secure attribute is set to a true value, then the
+        secure cookie option is included.
+        
+          >>> bim.thirdparty = False
+          >>> bim.cookieLifetime = None
+          >>> request = HTTPRequest(StringIO(''), {}, None)
+          >>> bim.secure = True
+          >>> bim.setRequestId(request, '1234')
+          >>> print request.response.getCookie(bim.namespace)
+          {'path': '/', 'secure': True, 'value': '1234'}
+
+
+        When the cookie is set, cache headers are added to the
+        response to try to prevent the cookie header from being cached:
+
+          >>> request.response.getHeader('Cache-Control')
+          'no-cache="Set-Cookie,Set-Cookie2"'
+          >>> request.response.getHeader('Pragma')
+          'no-cache'
+          >>> request.response.getHeader('Expires')
+          'Mon, 26 Jul 1997 05:00:00 GMT'
+
         """
         # TODO: Currently, the path is the ApplicationURL. This is reasonable,
         #     and will be adequate for most purposes.
@@ -326,22 +374,30 @@
         if self.thirdparty:
             logger.warning('ClientIdManager is using thirdparty cookies, '
                 'ignoring setIdRequest call')
-        else:
-            if self.cookieLifetime is not None:
-                if self.cookieLifetime:
-                    expires = build_http_date(time.time() + self.cookieLifetime)
-                else:
-                    expires = 'Tue, 19 Jan 2038 00:00:00 GMT'
-                request.response.setCookie(
-                        self.namespace, id, expires=expires,
-                        path=request.getApplicationURL(path_only=True)
-                        )
+            return
+
+        response = request.response
+        options = {}
+        if self.cookieLifetime is not None:
+            if self.cookieLifetime:
+                expires = build_http_date(time.time() + self.cookieLifetime)
             else:
-                request.response.setCookie(
-                        self.namespace, id,
-                        path=request.getApplicationURL(path_only=True)
-                        )
+                expires = 'Tue, 19 Jan 2038 00:00:00 GMT'
 
+            options['expires'] = expires
+
+        if self.secure:
+            options['secure'] = True
+
+        response.setCookie(
+            self.namespace, id,
+            path=request.getApplicationURL(path_only=True),
+            **options)
+
+        response.setHeader('Cache-Control', 'no-cache="Set-Cookie,Set-Cookie2"')
+        response.setHeader('Pragma', 'no-cache')
+        response.setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')
+
 def notifyVirtualHostChanged(event):
     """Adjust cookie paths when IVirtualHostRequest information changes.
 



More information about the Checkins mailing list