forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (pythonGH-18284)
The AbstractBasicAuthHandler class of the urllib.request module uses an inefficient regular expression which can be exploited by an attacker to cause a denial of service. Fix the regex to prevent the catastrophic backtracking. Vulnerability reported by Ben Caller and Matt Schwager. AbstractBasicAuthHandler of urllib.request now parses all WWW-Authenticate HTTP headers and accepts multiple challenges per header: use the realm of the first Basic challenge. Co-Authored-By: Serhiy Storchaka <[email protected]>
- Loading branch information
1 parent
d57cf55
commit 0b297d4
Showing
4 changed files
with
115 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1446,40 +1446,64 @@ def test_osx_proxy_bypass(self): | |
bypass = {'exclude_simple': True, 'exceptions': []} | ||
self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass)) | ||
|
||
def test_basic_auth(self, quote_char='"'): | ||
opener = OpenerDirector() | ||
password_manager = MockPasswordManager() | ||
auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager) | ||
realm = "ACME Widget Store" | ||
http_handler = MockHTTPHandler( | ||
401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' % | ||
(quote_char, realm, quote_char)) | ||
opener.add_handler(auth_handler) | ||
opener.add_handler(http_handler) | ||
self._test_basic_auth(opener, auth_handler, "Authorization", | ||
realm, http_handler, password_manager, | ||
"http://acme.example.com/protected", | ||
"http://acme.example.com/protected", | ||
) | ||
|
||
def test_basic_auth_with_single_quoted_realm(self): | ||
self.test_basic_auth(quote_char="'") | ||
|
||
def test_basic_auth_with_unquoted_realm(self): | ||
opener = OpenerDirector() | ||
password_manager = MockPasswordManager() | ||
auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager) | ||
realm = "ACME Widget Store" | ||
http_handler = MockHTTPHandler( | ||
401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm) | ||
opener.add_handler(auth_handler) | ||
opener.add_handler(http_handler) | ||
with self.assertWarns(UserWarning): | ||
def check_basic_auth(self, headers, realm): | ||
with self.subTest(realm=realm, headers=headers): | ||
opener = OpenerDirector() | ||
password_manager = MockPasswordManager() | ||
auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager) | ||
body = '\r\n'.join(headers) + '\r\n\r\n' | ||
http_handler = MockHTTPHandler(401, body) | ||
opener.add_handler(auth_handler) | ||
opener.add_handler(http_handler) | ||
self._test_basic_auth(opener, auth_handler, "Authorization", | ||
realm, http_handler, password_manager, | ||
"http://acme.example.com/protected", | ||
"http://acme.example.com/protected", | ||
) | ||
realm, http_handler, password_manager, | ||
"http://acme.example.com/protected", | ||
"http://acme.example.com/protected") | ||
|
||
def test_basic_auth(self): | ||
realm = "[email protected]" | ||
realm2 = "[email protected]" | ||
basic = f'Basic realm="{realm}"' | ||
basic2 = f'Basic realm="{realm2}"' | ||
other_no_realm = 'Otherscheme xxx' | ||
digest = (f'Digest realm="{realm2}", ' | ||
f'qop="auth, auth-int", ' | ||
f'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ' | ||
f'opaque="5ccc069c403ebaf9f0171e9517f40e41"') | ||
for realm_str in ( | ||
# test "quote" and 'quote' | ||
f'Basic realm="{realm}"', | ||
f"Basic realm='{realm}'", | ||
|
||
# charset is ignored | ||
f'Basic realm="{realm}", charset="UTF-8"', | ||
|
||
# Multiple challenges per header | ||
f'{basic}, {basic2}', | ||
f'{basic}, {other_no_realm}', | ||
f'{other_no_realm}, {basic}', | ||
f'{basic}, {digest}', | ||
f'{digest}, {basic}', | ||
): | ||
headers = [f'WWW-Authenticate: {realm_str}'] | ||
self.check_basic_auth(headers, realm) | ||
|
||
# no quote: expect a warning | ||
with support.check_warnings(("Basic Auth Realm was unquoted", | ||
UserWarning)): | ||
headers = [f'WWW-Authenticate: Basic realm={realm}'] | ||
self.check_basic_auth(headers, realm) | ||
|
||
# Multiple headers: one challenge per header. | ||
# Use the first Basic realm. | ||
for challenges in ( | ||
[basic, basic2], | ||
[basic, digest], | ||
[digest, basic], | ||
): | ||
headers = [f'WWW-Authenticate: {challenge}' | ||
for challenge in challenges] | ||
self.check_basic_auth(headers, realm) | ||
|
||
def test_proxy_basic_auth(self): | ||
opener = OpenerDirector() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
Misc/NEWS.d/next/Library/2020-03-25-16-02-16.bpo-39503.YmMbYn.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
:class:`~urllib.request.AbstractBasicAuthHandler` of :mod:`urllib.request` | ||
now parses all WWW-Authenticate HTTP headers and accepts multiple challenges | ||
per header: use the realm of the first Basic challenge. |
5 changes: 5 additions & 0 deletions
5
Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
CVE-2020-8492: The :class:`~urllib.request.AbstractBasicAuthHandler` class of the | ||
:mod:`urllib.request` module uses an inefficient regular expression which can | ||
be exploited by an attacker to cause a denial of service. Fix the regex to | ||
prevent the catastrophic backtracking. Vulnerability reported by Ben Caller | ||
and Matt Schwager. |