Mercurial > kallithea
changeset 5523:38d1c99cd000 stable
login: enhance came_from validation
Drop urlparse and just validate that came_from is a RFC 3986 compliant path.
This blocks an HTTP header injection vulnerability discovered by
Gjoko Krstic <gjoko@zeroscience.mk> of Zero Science Lab (CVE-2015-5285)
author | Søren Løvborg <sorenl@unity3d.com> |
---|---|
date | Wed, 23 Sep 2015 16:09:14 +0200 |
parents | 092ea4d40d60 |
children | 1346754f1852 |
files | kallithea/controllers/login.py kallithea/tests/functional/test_login.py |
diffstat | 2 files changed, 17 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/kallithea/controllers/login.py Wed Sep 30 23:19:46 2015 +0200 +++ b/kallithea/controllers/login.py Wed Sep 23 16:09:14 2015 +0200 @@ -27,8 +27,8 @@ import logging +import re import formencode -import urlparse from formencode import htmlfill from webob.exc import HTTPFound, HTTPBadRequest @@ -56,10 +56,19 @@ def __before__(self): super(LoginController, self).__before__() - def _validate_came_from(self, came_from): - """Return True if came_from is valid and can and should be used""" - url = urlparse.urlsplit(came_from) - return not url.scheme and not url.netloc + def _validate_came_from(self, came_from, + _re=re.compile(r"/(?!/)[-!#$%&'()*+,./:;=?@_~0-9A-Za-z]*$")): + """Return True if came_from is valid and can and should be used. + + Determines if a URI reference is valid and relative to the origin; + or in RFC 3986 terms, whether it matches this production: + + origin-relative-ref = path-absolute [ "?" query ] [ "#" fragment ] + + with the exception that '%' escapes are not validated and '#' is + allowed inside the fragment part. + """ + return _re.match(came_from) is not None def index(self): c.came_from = safe_str(request.GET.get('came_from', ''))
--- a/kallithea/tests/functional/test_login.py Wed Sep 30 23:19:46 2015 +0200 +++ b/kallithea/tests/functional/test_login.py Wed Sep 23 16:09:14 2015 +0200 @@ -107,6 +107,9 @@ ('ftp://ftp.example.com',), ('http://other.example.com/bl%C3%A5b%C3%A6rgr%C3%B8d',), ('//evil.example.com/',), + ('/\r\nX-Header-Injection: boo',), + ('/invälid_url_bytes',), + ('non-absolute-path',), ]) def test_login_bad_came_froms(self, url_came_from): response = self.app.post(url(controller='login', action='index',