Mercurial > kallithea
changeset 5143:43ad9c3b7d5d
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.
author | Andrew Shadura <andrew@shadura.me> |
---|---|
date | Wed, 04 Mar 2015 23:57:57 +0100 |
parents | 12e6de3d7e29 |
children | 7a5db341a942 |
files | kallithea/config/middleware.py kallithea/lib/middleware/sessionmiddleware.py |
diffstat | 2 files changed, 64 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/kallithea/config/middleware.py Wed Mar 25 10:11:54 2015 +0100 +++ b/kallithea/config/middleware.py Wed Mar 04 23:57:57 2015 +0100 @@ -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 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 @@ # 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']):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kallithea/lib/middleware/sessionmiddleware.py Wed Mar 04 23:57:57 2015 +0100 @@ -0,0 +1,62 @@ +# -*- 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 +""" + +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)