Skip to content

Commit 92e9b22

Browse files
committed
python-ecosys/requests: Security fix for leaked passwords.
With the old code, if you make a request with HTTP basic auth (a username/password) and did not specify a headers dict, then the cleartext username and password would be added to the default headers to be used for every subsequent HTTP request. That's probably not a good idea. This fixes it so the headers dict passed in, whether default or not, is never changed.
1 parent b28e481 commit 92e9b22

File tree

1 file changed

+19
-4
lines changed

1 file changed

+19
-4
lines changed

python-ecosys/requests/requests/__init__.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,23 @@ def request(
4747
redirect = None # redirection url, None means no redirection
4848
chunked_data = data and getattr(data, "__next__", None) and not getattr(data, "__len__", None)
4949

50+
# Security and correctness: Can't add extra fields to `headers`,
51+
# we have to have separate variables for them.
52+
#
53+
# This is because have `headers={}` in the function prototype.
54+
# So the same dictionary will get reused for every call that doesn't
55+
# explicitly specify a `headers` parameter.
56+
#
57+
# So if we put an Authorization header in `headers`, then following a
58+
# request to a site that requires a username and password, that same
59+
# plain-text username and password would be sent with every following HTTP
60+
# request that used the default for the `headers` parameter.
61+
basic_auth_header = None
5062
if auth is not None:
5163
import ubinascii
52-
5364
username, password = auth
54-
formated = b"{}:{}".format(username, password)
55-
formated = str(ubinascii.b2a_base64(formated)[:-1], "ascii")
56-
headers["Authorization"] = "Basic {}".format(formated)
65+
basic_auth_header = b"{}:{}".format(username, password)
66+
basic_auth_header = ubinascii.b2a_base64(basic_auth_header)[:-1]
5767

5868
try:
5969
proto, dummy, host, path = url.split("/", 3)
@@ -96,6 +106,11 @@ def request(
96106
s.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
97107
if "Host" not in headers:
98108
s.write(b"Host: %s\r\n" % host)
109+
if basic_auth_header:
110+
s.write(b"Authorization: Basic ")
111+
s.write(basic_auth_header)
112+
s.write(b"\r\n")
113+
99114
# Iterate over keys to avoid tuple alloc
100115
for k in headers:
101116
s.write(k)

0 commit comments

Comments
 (0)