Skip to content

Commit fe5bddd

Browse files
sethmlarsonmiss-islington
authored andcommitted
pythongh-90309: Base64-encode cookie values embedded in JS
(cherry picked from commit 76b3923) Co-authored-by: Seth Larson <seth@python.org>
1 parent e5d5541 commit fe5bddd

3 files changed

Lines changed: 27 additions & 13 deletions

File tree

Lib/http/cookies.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,17 +391,21 @@ def __repr__(self):
391391
return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
392392

393393
def js_output(self, attrs=None):
394+
import base64
394395
# Print javascript
395396
output_string = self.OutputString(attrs)
396397
if _has_control_character(output_string):
397398
raise CookieError("Control characters are not allowed in cookies")
399+
# Base64-encode value to avoid template
400+
# injection in cookie values.
401+
output_encoded = base64.b64encode(output_string.encode('utf-8')).decode("ascii")
398402
return """
399403
<script type="text/javascript">
400404
<!-- begin hiding
401-
document.cookie = \"%s\";
405+
document.cookie = atob(\"%s\");
402406
// end hiding -->
403407
</script>
404-
""" % (output_string.replace('"', r'\"'))
408+
""" % (output_encoded,)
405409

406410
def OutputString(self, attrs=None):
407411
# Build up our result

Lib/test/test_http_cookies.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Simple test suite for http/cookies.py
2-
2+
import base64
33
import copy
44
import unittest
55
import doctest
@@ -152,17 +152,19 @@ def test_load(self):
152152

153153
self.assertEqual(C.output(['path']),
154154
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
155-
self.assertEqual(C.js_output(), r"""
155+
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme; Version=1').decode('ascii')
156+
self.assertEqual(C.js_output(), fr"""
156157
<script type="text/javascript">
157158
<!-- begin hiding
158-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
159+
document.cookie = atob("{cookie_encoded}");
159160
// end hiding -->
160161
</script>
161162
""")
162-
self.assertEqual(C.js_output(['path']), r"""
163+
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme').decode('ascii')
164+
self.assertEqual(C.js_output(['path']), fr"""
163165
<script type="text/javascript">
164166
<!-- begin hiding
165-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
167+
document.cookie = atob("{cookie_encoded}");
166168
// end hiding -->
167169
</script>
168170
""")
@@ -267,17 +269,19 @@ def test_quoted_meta(self):
267269

268270
self.assertEqual(C.output(['path']),
269271
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
270-
self.assertEqual(C.js_output(), r"""
272+
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1').decode('ascii')
273+
self.assertEqual(C.js_output(), fr"""
271274
<script type="text/javascript">
272275
<!-- begin hiding
273-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
276+
document.cookie = atob("{expected_encoded_cookie}");
274277
// end hiding -->
275278
</script>
276279
""")
277-
self.assertEqual(C.js_output(['path']), r"""
280+
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii')
281+
self.assertEqual(C.js_output(['path']), fr"""
278282
<script type="text/javascript">
279283
<!-- begin hiding
280-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
284+
document.cookie = atob("{expected_encoded_cookie}");
281285
// end hiding -->
282286
</script>
283287
""")
@@ -368,13 +372,16 @@ def test_setter(self):
368372
self.assertEqual(
369373
M.output(),
370374
"Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i))
375+
expected_encoded_cookie = base64.b64encode(
376+
("%s=%s; Path=/foo" % (i, "%s_coded_val" % i)).encode("ascii")
377+
).decode('ascii')
371378
expected_js_output = """
372379
<script type="text/javascript">
373380
<!-- begin hiding
374-
document.cookie = "%s=%s; Path=/foo";
381+
document.cookie = atob("%s");
375382
// end hiding -->
376383
</script>
377-
""" % (i, "%s_coded_val" % i)
384+
""" % (expected_encoded_cookie,)
378385
self.assertEqual(M.js_output(), expected_js_output)
379386
for i in ["foo bar", "foo@bar"]:
380387
# Try some illegal characters
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Base64-encode values when embedding cookies to JavaScript using the
2+
:meth:`http.cookies.BaseCookie.js_output` method to avoid injection
3+
and escaping.

0 commit comments

Comments
 (0)