Skip to content

Commit a67e6c8

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 1b2301c commit a67e6c8

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
@@ -389,17 +389,21 @@ def __repr__(self):
389389
return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
390390

391391
def js_output(self, attrs=None):
392+
import base64
392393
# Print javascript
393394
output_string = self.OutputString(attrs)
394395
if _has_control_character(output_string):
395396
raise CookieError("Control characters are not allowed in cookies")
397+
# Base64-encode value to avoid template
398+
# injection in cookie values.
399+
output_encoded = base64.b64encode(output_string.encode('utf-8')).decode("ascii")
396400
return """
397401
<script type="text/javascript">
398402
<!-- begin hiding
399-
document.cookie = \"%s\";
403+
document.cookie = atob(\"%s\");
400404
// end hiding -->
401405
</script>
402-
""" % (output_string.replace('"', r'\"'))
406+
""" % (output_encoded,)
403407

404408
def OutputString(self, attrs=None):
405409
# 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
@@ -153,17 +153,19 @@ def test_load(self):
153153

154154
self.assertEqual(C.output(['path']),
155155
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
156-
self.assertEqual(C.js_output(), r"""
156+
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme; Version=1').decode('ascii')
157+
self.assertEqual(C.js_output(), fr"""
157158
<script type="text/javascript">
158159
<!-- begin hiding
159-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
160+
document.cookie = atob("{cookie_encoded}");
160161
// end hiding -->
161162
</script>
162163
""")
163-
self.assertEqual(C.js_output(['path']), r"""
164+
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme').decode('ascii')
165+
self.assertEqual(C.js_output(['path']), fr"""
164166
<script type="text/javascript">
165167
<!-- begin hiding
166-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
168+
document.cookie = atob("{cookie_encoded}");
167169
// end hiding -->
168170
</script>
169171
""")
@@ -260,17 +262,19 @@ def test_quoted_meta(self):
260262

261263
self.assertEqual(C.output(['path']),
262264
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
263-
self.assertEqual(C.js_output(), r"""
265+
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1').decode('ascii')
266+
self.assertEqual(C.js_output(), fr"""
264267
<script type="text/javascript">
265268
<!-- begin hiding
266-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
269+
document.cookie = atob("{expected_encoded_cookie}");
267270
// end hiding -->
268271
</script>
269272
""")
270-
self.assertEqual(C.js_output(['path']), r"""
273+
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii')
274+
self.assertEqual(C.js_output(['path']), fr"""
271275
<script type="text/javascript">
272276
<!-- begin hiding
273-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
277+
document.cookie = atob("{expected_encoded_cookie}");
274278
// end hiding -->
275279
</script>
276280
""")
@@ -361,13 +365,16 @@ def test_setter(self):
361365
self.assertEqual(
362366
M.output(),
363367
"Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i))
368+
expected_encoded_cookie = base64.b64encode(
369+
("%s=%s; Path=/foo" % (i, "%s_coded_val" % i)).encode("ascii")
370+
).decode('ascii')
364371
expected_js_output = """
365372
<script type="text/javascript">
366373
<!-- begin hiding
367-
document.cookie = "%s=%s; Path=/foo";
374+
document.cookie = atob("%s");
368375
// end hiding -->
369376
</script>
370-
""" % (i, "%s_coded_val" % i)
377+
""" % (expected_encoded_cookie,)
371378
self.assertEqual(M.js_output(), expected_js_output)
372379
for i in ["foo bar", "foo@bar"]:
373380
# 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)