[PATCH v2] middleware: use secure cookies over secure connections

Andrew Shadura andrew at shadura.me
Mon May 11 10:26:07 EDT 2015


# HG changeset patch
# User Andrew Shadura <andrew at shadura.me>
# Date 1425509877 -3600
#      Wed Mar 04 23:57:57 2015 +0100
# Node ID c187c0a2bc0290e1f226e8cff342d836b0bcb42e
# Parent  572f62542047ded9f814af5db388dadec6425dc9
middleware: use secure cookies over secure connections

HTTP cookie spec defines secure cookies, which are transmitted only over secure
connections (HTTPS). Using them helps protect against some attacks, but cookies
shouldn't be made secure when we don't have HTTPS configured. As it is now, it's
left at user's discretion, but probably it's a good idea to force secure cookies
when they can be used.

In the current implementation, cookies are issued to users before they actually
try to log in, on the first page load. So if that happens over HTTPS, it's
probably safe to assume secure cookies can be used, and to default to normal
"insecure" cookies if HTTPS isn't available.

It's not easy to sneak into Beaker's internals, and it doesn't support selective
secureness, so we use our own wrapper around Beaker's SessionMiddleware class to
give secure cookies over HTTPS connections. Beaker's built-in mechanism for
secure cookies is forced to add the flag when needed only.

We also force httponly flag on cookies as we don't want javascripts to see them.

diff --git a/kallithea/config/middleware.py b/kallithea/config/middleware.py
--- a/kallithea/config/middleware.py
+++ b/kallithea/config/middleware.py
@@ -15,7 +15,6 @@
     Pylons middleware initialization
 """
 
-from beaker.middleware import SessionMiddleware
 from routes.middleware import RoutesMiddleware
 from paste.cascade import Cascade
 from paste.registry import RegistryManager
@@ -29,6 +28,7 @@ from pylons.wsgiapp import PylonsApp
 from kallithea.lib.middleware.simplehg import SimpleHg
 from kallithea.lib.middleware.simplegit import SimpleGit
 from kallithea.lib.middleware.https_fixup import HttpsFixup
+from kallithea.lib.middleware.sessionmiddleware import SessionMiddleware
 from kallithea.config.environment import load_environment
 from kallithea.lib.middleware.wrapper import RequestWrapper
 
diff --git a/kallithea/lib/middleware/sessionmiddleware.py b/kallithea/lib/middleware/sessionmiddleware.py
new file mode 100644
--- /dev/null
+++ b/kallithea/lib/middleware/sessionmiddleware.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+kallithea.lib.middleware.sessionmiddleware
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+session management middleware
+
+This file overrides Beaker's built-in SessionMiddleware
+class to automagically use secure cookies over HTTPS.
+
+Original Beaker SessionMiddleware class written by Ben Bangert
+
+:created_on: March 04, 2015
+:author: andrewsh
+:copyright: (c) 2015 Andrew Shadura
+:license: GPLv3, see LICENSE.md for more details.
+"""
+
+from beaker.session import SessionObject
+from beaker.middleware import SessionMiddleware as BeakerSessionMiddleware
+
+class SessionMiddleware(BeakerSessionMiddleware):
+    def __init__(self, wrap_app, config=None, environ_key='beaker.session',
+                **kwargs):
+        """
+        Initialise the session middleware
+
+        Call Beaker's original constructor to set the options, then
+        unset secure option as we're handling that on our own and don't
+        want Beaker to interfere.
+        """
+        super(SessionMiddleware, self).__init__(wrap_app, config,
+            environ_key, **kwargs)
+        self.options["secure"] = False
+        self.options["httponly"] = True
+
+    def __call__(self, environ, start_response):
+        """
+        This function's implementation is taken directly from Beaker,
+        with HTTPS detection added. When accessed over HTTPS, force
+        setting cookie's secure flag.
+        """
+        options = dict(self.options)
+        options["secure"] = environ['wsgi.url_scheme'] == 'https'
+        session = SessionObject(environ, **options)
+        if environ.get('paste.registry'):
+            if environ['paste.registry'].reglist:
+                environ['paste.registry'].register(self.session, session)
+        environ[self.environ_key] = session
+        environ['beaker.get_session'] = self._get_session
+
+        if 'paste.testing_variables' in environ and 'webtest_varname' in options:
+            environ['paste.testing_variables'][options['webtest_varname']] = session
+
+        def session_start_response(status, headers, exc_info=None):
+            if session.accessed():
+                session.persist()
+                if session.__dict__['_headers']['set_cookie']:
+                    cookie = session.__dict__['_headers']['cookie_out']
+                    if cookie:
+                        headers.append(('Set-cookie', cookie))
+            return start_response(status, headers, exc_info)
+        return self.wrap_app(environ, session_start_response)


More information about the kallithea-general mailing list