Merge Issue 11662 from 3.2 branch.

This commit is contained in:
guido@google.com 2011-03-29 12:02:49 -07:00
commit 2008a8f8c0
5 changed files with 74 additions and 0 deletions

View File

@ -650,6 +650,10 @@ HTTPRedirectHandler Objects
is the case, :exc:`HTTPError` is raised. See :rfc:`2616` for details of the is the case, :exc:`HTTPError` is raised. See :rfc:`2616` for details of the
precise meanings of the various redirection codes. 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) .. method:: HTTPRedirectHandler.redirect_request(req, fp, code, msg, hdrs, newurl)

View File

@ -2,6 +2,7 @@
import urllib.parse import urllib.parse
import urllib.request import urllib.request
import urllib.error
import http.client import http.client
import email.message import email.message
import io import io
@ -206,6 +207,21 @@ Content-Type: text/html; charset=iso-8859-1
finally: finally:
self.unfakehttp() 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): def test_empty_socket(self):
# urlopen() raises IOError if the underlying socket does not send any # urlopen() raises IOError if the underlying socket does not send any
# data. (#1680230) # data. (#1680230)

View File

@ -10,6 +10,7 @@ import urllib.request
# The proxy bypass method imported below has logic specific to the OSX # The proxy bypass method imported below has logic specific to the OSX
# proxy config data structure but is testable on all platforms. # proxy config data structure but is testable on all platforms.
from urllib.request import Request, OpenerDirector, _proxy_bypass_macosx_sysconf from urllib.request import Request, OpenerDirector, _proxy_bypass_macosx_sysconf
import urllib.error
# XXX # XXX
# Request # Request
@ -1031,6 +1032,29 @@ class HandlerTests(unittest.TestCase):
self.assertEqual(count, self.assertEqual(count,
urllib.request.HTTPRedirectHandler.max_redirections) 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): def test_cookie_redirect(self):
# cookies shouldn't leak into redirected requests # cookies shouldn't leak into redirected requests
from http.cookiejar import CookieJar from http.cookiejar import CookieJar

View File

@ -545,6 +545,17 @@ class HTTPRedirectHandler(BaseHandler):
# fix a possible malformed URL # fix a possible malformed URL
urlparts = urlparse(newurl) 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: if not urlparts.path:
urlparts = list(urlparts) urlparts = list(urlparts)
urlparts[2] = "/" urlparts[2] = "/"
@ -1903,8 +1914,24 @@ class FancyURLopener(URLopener):
return return
void = fp.read() void = fp.read()
fp.close() fp.close()
# In case the server sent a relative URL, join with original: # In case the server sent a relative URL, join with original:
newurl = urljoin(self.type + ":" + url, newurl) 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) return self.open(newurl)
def http_error_301(self, url, fp, errcode, errmsg, headers, data=None): def http_error_301(self, url, fp, errcode, errmsg, headers, data=None):

View File

@ -84,6 +84,9 @@ Core and Builtins
Library Library
------- -------
- Issue #11662: Make urllib and urllib2 ignore redirections if the
scheme is not HTTP, HTTPS or FTP (CVE-2011-1521).
- Issue #6811: Allow importlib to change a code object's co_filename attribute - Issue #6811: Allow importlib to change a code object's co_filename attribute
to match the path to where the source code currently is, not where the code to match the path to where the source code currently is, not where the code
object originally came from. object originally came from.