Issue 11662: Fix vulnerability in urllib/urllib2.
(This version is a cleaned-up backport of a fix by Senthil Kumaran.)
This commit is contained in:
parent
b938c8c253
commit
a119df91f3
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue