]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue 11662: Fix vulnerability in urllib/urllib2.
authorguido@google.com <guido@google.com>
Tue, 29 Mar 2011 18:41:02 +0000 (11:41 -0700)
committerguido@google.com <guido@google.com>
Tue, 29 Mar 2011 18:41:02 +0000 (11:41 -0700)
(This version is a cleaned-up backport of a fix by Senthil Kumaran.)

Doc/library/urllib.request.rst
Lib/test/test_urllib.py
Lib/test/test_urllib2.py
Lib/urllib/request.py
Misc/NEWS

index fe61030f3511b7fdc57c90efc8a6d2bd08fb05dd..b4a7f288c34b36dca1fd378c9550c808afd17784 100644 (file)
@@ -783,6 +783,10 @@ HTTPRedirectHandler Objects
    is the case, :exc:`HTTPError` is raised.  See :rfc:`2616` for details of the
    precise meanings of the various redirection codes.
 
+   An :class:`HTTPError` exception raised as a security consideration if the
+   HTTPRedirectHandler is presented with a redirected url which is not an HTTP,
+   HTTPS or FTP url.
+
 
 .. method:: HTTPRedirectHandler.redirect_request(req, fp, code, msg, hdrs, newurl)
 
index 422831e35fa7fb4ceeb9a810b211e075244e7165..2b8852127bc7571edca5b18cb6cd5995622ba95f 100644 (file)
@@ -2,6 +2,7 @@
 
 import urllib.parse
 import urllib.request
+import urllib.error
 import http.client
 import email.message
 import io
@@ -183,6 +184,21 @@ Content-Type: text/html; charset=iso-8859-1
         finally:
             self.unfakehttp()
 
+    def test_invalid_redirect(self):
+        # urlopen() should raise IOError for many error codes.
+        self.fakehttp(b'''HTTP/1.1 302 Found
+Date: Wed, 02 Jan 2008 03:03:54 GMT
+Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
+Location: file://guidocomputer.athome.com:/python/license
+Connection: close
+Content-Type: text/html; charset=iso-8859-1
+''')
+        try:
+            self.assertRaises(urllib.error.HTTPError, urlopen,
+                              "http://python.org/")
+        finally:
+            self.unfakehttp()
+
     def test_empty_socket(self):
         # urlopen() raises IOError if the underlying socket does not send any
         # data. (#1680230)
index 1704683b4dd61ac357ccb6ba98ccb90d80389584..3fd7baa8ddc072a76561cfe6b556c57488061bef 100644 (file)
@@ -9,6 +9,7 @@ import urllib.request
 # The proxy bypass method imported below has logic specific to the OSX
 # proxy config data structure but is testable on all platforms.
 from urllib.request import Request, OpenerDirector, _proxy_bypass_macosx_sysconf
+import urllib.error
 
 # XXX
 # Request
@@ -985,6 +986,29 @@ class HandlerTests(unittest.TestCase):
             self.assertEqual(count,
                              urllib.request.HTTPRedirectHandler.max_redirections)
 
+
+    def test_invalid_redirect(self):
+        from_url = "http://example.com/a.html"
+        valid_schemes = ['http','https','ftp']
+        invalid_schemes = ['file','imap','ldap']
+        schemeless_url = "example.com/b.html"
+        h = urllib.request.HTTPRedirectHandler()
+        o = h.parent = MockOpener()
+        req = Request(from_url)
+        req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
+
+        for scheme in invalid_schemes:
+            invalid_url = scheme + '://' + schemeless_url
+            self.assertRaises(urllib.error.HTTPError, h.http_error_302,
+                    req, MockFile(), 302, "Security Loophole",
+                    MockHeaders({"location": invalid_url}))
+
+        for scheme in valid_schemes:
+            valid_url = scheme + '://' + schemeless_url
+            h.http_error_302(req, MockFile(), 302, "That's fine",
+                MockHeaders({"location": valid_url}))
+            self.assertEqual(o.req.get_full_url(), valid_url)
+
     def test_cookie_redirect(self):
         # cookies shouldn't leak into redirected requests
         from http.cookiejar import CookieJar
index 087e9a606c04e3c4b1ff7919397f9986607a732c..220dfe4b19bd17ec4386fe48640124cff3e06a46 100644 (file)
@@ -528,6 +528,17 @@ class HTTPRedirectHandler(BaseHandler):
 
         # fix a possible malformed URL
         urlparts = urlparse(newurl)
+
+        # For security reasons we don't allow redirection to anything other
+        # than http, https or ftp.
+
+        if not urlparts.scheme in ('http', 'https', 'ftp'):
+            raise HTTPError(newurl, code,
+                            msg +
+                            " - Redirection to url '%s' is not allowed" %
+                            newurl,
+                            headers, fp)
+
         if not urlparts.path:
             urlparts = list(urlparts)
             urlparts[2] = "/"
@@ -1864,8 +1875,24 @@ class FancyURLopener(URLopener):
             return
         void = fp.read()
         fp.close()
+
         # In case the server sent a relative URL, join with original:
         newurl = urljoin(self.type + ":" + url, newurl)
+
+        urlparts = urlparse(newurl)
+
+        # For security reasons, we don't allow redirection to anything other
+        # than http, https and ftp.
+
+        # We are using newer HTTPError with older redirect_internal method
+        # This older method will get deprecated in 3.3
+
+        if not urlparts.scheme in ('http', 'https', 'ftp'):
+            raise HTTPError(newurl, errcode,
+                            errmsg +
+                            " Redirection to url '%s' is not allowed." % newurl,
+                            headers, fp)
+
         return self.open(newurl)
 
     def http_error_301(self, url, fp, errcode, errmsg, headers, data=None):
index fca77ef65e3bb6351f5e7f83ccf7b9696bffcfb9..91d9ce6081e2646efe1286d8d5970c89aea5f3e3 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -44,6 +44,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #11662: Make urllib and urllib2 ignore redirections if the
+  scheme is not HTTP, HTTPS or FTP (CVE-2011-1521).
+
 - Issue #5537: Fix time2isoz() and time2netscape() functions of
   httplib.cookiejar for expiration year greater than 2038 on 32-bit systems.