Changeset - 38d1c99cd000
[Not reviewed]
stable
0 2 0
Søren Løvborg - 10 years ago 2015-09-23 16:09:14
sorenl@unity3d.com
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)
2 files changed with 17 insertions and 5 deletions:
0 comments (0 inline, 0 general)
kallithea/controllers/login.py
Show inline comments
 
@@ -27,8 +27,8 @@ Original author and date, and relevant c
 

	
 

	
 
import logging
 
import re
 
import formencode
 
import urlparse
 

	
 
from formencode import htmlfill
 
from webob.exc import HTTPFound, HTTPBadRequest
 
@@ -56,10 +56,19 @@ class LoginController(BaseController):
 
    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', ''))
kallithea/tests/functional/test_login.py
Show inline comments
 
@@ -107,6 +107,9 @@ class TestLoginController(TestController
 
          ('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',
0 comments (0 inline, 0 general)