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

Andrew Shadura andrew at shadura.me
Tue May 12 06:35:19 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 8d660b29559be70b2edf7fc63b8307225f59520c
# 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.

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 SecureSessionMiddleware
 from kallithea.config.environment import load_environment
 from kallithea.lib.middleware.wrapper import RequestWrapper
@@ -60,7 +60,7 @@ def make_app(global_conf, full_stack=Tru
     # Routing/Session/Cache Middleware
     app = RoutesMiddleware(app, config['routes.map'])
-    app = SessionMiddleware(app, config)
+    app = SecureSessionMiddleware(app, config)
     # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
     if asbool(config['pdebug']):
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,67 @@
+# -*- 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
+# 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/>.
+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
+class SecureSessionMiddleware(SessionMiddleware):
+    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.
+        The only difference from that original code is that we switch
+        the secure option on and off depending on the URL scheme (first
+        two lines). To avoid concurrency issues, we use a local options
+        variable.
+        """
+        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