https://github.com/python/cpython/commit/76b3923d688c0efc580658476c5f525ec8735104
commit: 76b3923d688c0efc580658476c5f525ec8735104
branch: main
author: Seth Larson <[email protected]>
committer: sethmlarson <[email protected]>
date: 2026-04-22T19:22:31Z
summary:
gh-90309: Base64-encode cookie values embedded in JS
files:
A Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst
M Lib/http/cookies.py
M Lib/test/test_http_cookies.py
diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py
index 769541116993c4..660fec4f1be865 100644
--- a/Lib/http/cookies.py
+++ b/Lib/http/cookies.py
@@ -391,17 +391,21 @@ def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
def js_output(self, attrs=None):
+ import base64
# Print javascript
output_string = self.OutputString(attrs)
if _has_control_character(output_string):
raise CookieError("Control characters are not allowed in cookies")
+ # Base64-encode value to avoid template
+ # injection in cookie values.
+ output_encoded =
base64.b64encode(output_string.encode('utf-8')).decode("ascii")
return """
<script type="text/javascript">
<!-- begin hiding
- document.cookie = \"%s\";
+ document.cookie = atob(\"%s\");
// end hiding -->
</script>
- """ % (output_string.replace('"', r'\"'))
+ """ % (output_encoded,)
def OutputString(self, attrs=None):
# Build up our result
diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py
index e2c7551c0b3341..cfcbc17bd6df80 100644
--- a/Lib/test/test_http_cookies.py
+++ b/Lib/test/test_http_cookies.py
@@ -1,5 +1,5 @@
# Simple test suite for http/cookies.py
-
+import base64
import copy
import unittest
import doctest
@@ -175,17 +175,19 @@ def test_load(self):
self.assertEqual(C.output(['path']),
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
- self.assertEqual(C.js_output(), r"""
+ cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE";
Path=/acme; Version=1').decode('ascii')
+ self.assertEqual(C.js_output(), fr"""
<script type="text/javascript">
<!-- begin hiding
- document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
+ document.cookie = atob("{cookie_encoded}");
// end hiding -->
</script>
""")
- self.assertEqual(C.js_output(['path']), r"""
+ cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE";
Path=/acme').decode('ascii')
+ self.assertEqual(C.js_output(['path']), fr"""
<script type="text/javascript">
<!-- begin hiding
- document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
+ document.cookie = atob("{cookie_encoded}");
// end hiding -->
</script>
""")
@@ -290,17 +292,19 @@ def test_quoted_meta(self):
self.assertEqual(C.output(['path']),
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
- self.assertEqual(C.js_output(), r"""
+ expected_encoded_cookie =
base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme;
Version=1').decode('ascii')
+ self.assertEqual(C.js_output(), fr"""
<script type="text/javascript">
<!-- begin hiding
- document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
+ document.cookie = atob("{expected_encoded_cookie}");
// end hiding -->
</script>
""")
- self.assertEqual(C.js_output(['path']), r"""
+ expected_encoded_cookie =
base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii')
+ self.assertEqual(C.js_output(['path']), fr"""
<script type="text/javascript">
<!-- begin hiding
- document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
+ document.cookie = atob("{expected_encoded_cookie}");
// end hiding -->
</script>
""")
@@ -391,13 +395,16 @@ def test_setter(self):
self.assertEqual(
M.output(),
"Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i))
+ expected_encoded_cookie = base64.b64encode(
+ ("%s=%s; Path=/foo" % (i, "%s_coded_val" % i)).encode("ascii")
+ ).decode('ascii')
expected_js_output = """
<script type="text/javascript">
<!-- begin hiding
- document.cookie = "%s=%s; Path=/foo";
+ document.cookie = atob("%s");
// end hiding -->
</script>
- """ % (i, "%s_coded_val" % i)
+ """ % (expected_encoded_cookie,)
self.assertEqual(M.js_output(), expected_js_output)
for i in ["foo bar", "foo@bar"]:
# Try some illegal characters
diff --git
a/Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst
b/Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst
new file mode 100644
index 00000000000000..d7d376737e4ad1
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst
@@ -0,0 +1,3 @@
+Base64-encode values when embedding cookies to JavaScript using the
+:meth:`http.cookies.BaseCookie.js_output` method to avoid injection
+and escaping.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]