bpo-46756: Fix authorization check in urllib.request (GH-31353)

Fix a bug in urllib.request.HTTPPasswordMgr.find_user_password() and
urllib.request.HTTPPasswordMgrWithPriorAuth.is_authenticated() which
allowed to bypass authorization. For example, access to URI "example.org/foobar"
was allowed if the user was authorized for URI "example.org/foo".
This commit is contained in:
Serhiy Storchaka 2022-02-25 13:31:03 +02:00 committed by GitHub
parent 53ecf9e08d
commit e2e72567a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 8 deletions

View File

@ -164,7 +164,6 @@ class RequestHdrsTests(unittest.TestCase):
self.assertEqual(find_user_pass("Some Realm",
"http://example.com/spam"),
('joe', 'password'))
self.assertEqual(find_user_pass("Some Realm",
"http://example.com/spam/spam"),
('joe', 'password'))
@ -173,12 +172,29 @@ class RequestHdrsTests(unittest.TestCase):
add("c", "http://example.com/foo", "foo", "ni")
add("c", "http://example.com/bar", "bar", "nini")
add("c", "http://example.com/foo/bar", "foobar", "nibar")
self.assertEqual(find_user_pass("c", "http://example.com/foo"),
('foo', 'ni'))
self.assertEqual(find_user_pass("c", "http://example.com/bar"),
('bar', 'nini'))
self.assertEqual(find_user_pass("c", "http://example.com/foo/"),
('foo', 'ni'))
self.assertEqual(find_user_pass("c", "http://example.com/foo/bar"),
('foo', 'ni'))
self.assertEqual(find_user_pass("c", "http://example.com/foo/baz"),
('foo', 'ni'))
self.assertEqual(find_user_pass("c", "http://example.com/foobar"),
(None, None))
add("c", "http://example.com/baz/", "baz", "ninini")
self.assertEqual(find_user_pass("c", "http://example.com/baz"),
(None, None))
self.assertEqual(find_user_pass("c", "http://example.com/baz/"),
('baz', 'ninini'))
self.assertEqual(find_user_pass("c", "http://example.com/baz/bar"),
('baz', 'ninini'))
# For the same path, newer password should be considered.
@ -1658,8 +1674,9 @@ class HandlerTests(unittest.TestCase):
auth_prior_handler.add_password(
None, request_url, user, password, is_authenticated=True)
is_auth = pwd_manager.is_authenticated(request_url)
self.assertTrue(is_auth)
self.assertTrue(pwd_manager.is_authenticated(request_url))
self.assertTrue(pwd_manager.is_authenticated(request_url + '/nested'))
self.assertFalse(pwd_manager.is_authenticated(request_url + 'plain'))
opener = OpenerDirector()
opener.add_handler(auth_prior_handler)

View File

@ -889,10 +889,10 @@ class HTTPPasswordMgr:
return True
if base[0] != test[0]:
return False
common = posixpath.commonprefix((base[1], test[1]))
if len(common) == len(base[1]):
return True
return False
prefix = base[1]
if prefix[-1:] != '/':
prefix += '/'
return test[1].startswith(prefix)
class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):

View File

@ -0,0 +1,5 @@
Fix a bug in :meth:`urllib.request.HTTPPasswordMgr.find_user_password` and
:meth:`urllib.request.HTTPPasswordMgrWithPriorAuth.is_authenticated` which
allowed to bypass authorization. For example, access to URI
``example.org/foobar`` was allowed if the user was authorized for URI
``example.org/foo``.