The cookie value is quoted by the Werkzeug library to be safe for use in Cookie headers; this includes quoting any commas, semicolons, double quotes and backslashes:

cookie_quoting_map = {
    b',' : b'\\054',
    b';' : b'\\073',
    b'"' : b'\\"',
    b'\\' : b'\\\\',
}

Anything else outside of letters, digits and the characters !#%&'~_`><@,:/$*+-.^|)(?}{= is encoded to an octal codepoint as well. If there are any escaped values in the cookie, the whole cookie is also surrounded by double quotes.

If you need access to the cookie value in JavaScript, you'll have to decode this again. Values that start with a slash and 3 digits are octal values; a String.replace() call should do:

function decode_flask_cookie(val) {
    if (val.indexOf('\\') === -1) {
        return val;  // not encoded
    }
    val = val.slice(1, -1).replace(/\\"/g, '"');
    val = val.replace(/\\(\d{3})/g, function(match, octal) { 
        return String.fromCharCode(parseInt(octal, 8));
    });
    return val.replace(/\\\\/g, '\\');
}

Demo:

> // recreate properly escaped value
> var cookie = "\"{\\\"message\\\": \\\"hello\\\"\\054 \\\"type\\\": \\\"success\\\"}\""
> cookie
""{\"message\": \"hello\"\054 \"type\": \"success\"}""
> decode_flask_cookie(cookie)
"{"message": "hello", "type": "success"}"
> JSON.parse(decode_flask_cookie(cookie))
Object {message: "hello", type: "success"}
Answer from Martijn Pieters on Stack Overflow
🌐
Beautiful Soup
tedboy.github.io › flask › generated › werkzeug.parse_cookie.html
werkzeug.parse_cookie — Flask API
Parse a cookie. Either from a string or WSGI environ · Per default encoding errors are ignored. If you want a different behavior you can set errors to 'replace' or 'strict'. In strict mode a HTTPUnicodeError is raised
🌐
Werkzeug
werkzeug.palletsprojects.com › en › stable › test
Testing WSGI Applications — Werkzeug Documentation (3.1.x)
class werkzeug.test.Cookie(key, value, decoded_key, decoded_value, expires, max_age, domain, origin_only, path, secure, http_only, same_site)¶
Top answer
1 of 3
6

The cookie value is quoted by the Werkzeug library to be safe for use in Cookie headers; this includes quoting any commas, semicolons, double quotes and backslashes:

cookie_quoting_map = {
    b',' : b'\\054',
    b';' : b'\\073',
    b'"' : b'\\"',
    b'\\' : b'\\\\',
}

Anything else outside of letters, digits and the characters !#%&'~_`><@,:/$*+-.^|)(?}{= is encoded to an octal codepoint as well. If there are any escaped values in the cookie, the whole cookie is also surrounded by double quotes.

If you need access to the cookie value in JavaScript, you'll have to decode this again. Values that start with a slash and 3 digits are octal values; a String.replace() call should do:

function decode_flask_cookie(val) {
    if (val.indexOf('\\') === -1) {
        return val;  // not encoded
    }
    val = val.slice(1, -1).replace(/\\"/g, '"');
    val = val.replace(/\\(\d{3})/g, function(match, octal) { 
        return String.fromCharCode(parseInt(octal, 8));
    });
    return val.replace(/\\\\/g, '\\');
}

Demo:

> // recreate properly escaped value
> var cookie = "\"{\\\"message\\\": \\\"hello\\\"\\054 \\\"type\\\": \\\"success\\\"}\""
> cookie
""{\"message\": \"hello\"\054 \"type\": \"success\"}""
> decode_flask_cookie(cookie)
"{"message": "hello", "type": "success"}"
> JSON.parse(decode_flask_cookie(cookie))
Object {message: "hello", type: "success"}
2 of 3
0

Dealing with the same issue today, but it seems the facts on the ground have changed.

First of all, sending the cookie back is as simple as:

cookie_str = json.dumps(mydictionary, separators=(',', ':'))
resp.set_cookie('cookiename', cookie_str, None, None, '/') 

On the Javascript side, in browser, I read the cookie as follows using the js-cookies project:

var cookie2 = Cookies.noConflict();
var cookiejs = cookie2.getJSON('cookiename');
var one = ca.field1;
var two = ca.field2;

It does appear that the curly's are not getting escaped properly in my case. As a result, I have to find and replace the curley's with escaped versions \{ and \}. However, after that, instead of using getJSON, I had to resort to jquery's $.parseJSON() function, which returns a usable json object.

Still haven't figured out how to get the cookie json string to behave correctly without re-escaping the curley's on the javascript side. The escaping and jquery method is a little hacky... but it works.

Also I'm using Zappa-Flask in AWS Lambda, not sure if that is messing up my cookie.

🌐
Degoogle
kirsle.net › wizards › flask-session.cgi
Flask Session Cookie Decoder
"""Flask session cookie decoder.""" #import cgitb #cgitb.enable() import os import codecs import cgi import jinja2 from base64 import b64decode from itsdangerous import base64_decode import zlib import json import pygments from pygments.formatters import HtmlFormatter from pygments.lexers import PythonLexer, JsonLexer import uuid from werkzeug.http import parse_date from flask import Markup env = jinja2.Environment(loader=jinja2.FileSystemLoader(".")) def main(): form = get_form() # Pygments CSS source.
🌐
GitHub
github.com › pallets › werkzeug › blob › main › src › werkzeug › http.py
werkzeug/src/werkzeug/http.py at main · pallets/werkzeug
cookie = cookie.encode("latin1").decode() · return _sansio_http.parse_cookie(cookie=cookie, cls=cls) ·
Author   pallets
🌐
GitHub
github.com › noraj › flask-session-cookie-manager
GitHub - noraj/flask-session-cookie-manager: :cookie: Flask Session Cookie Decoder/Encoder · GitHub
$ python{2,3} flask_session_cookie_manager{2,3}.py decode -c 'eyJudW1iZXIiOnsiIGIiOiJNekkyTkRFd01ETXhOVEExIn0sInVzZXJuYW1lIjp7IiBiIjoiWVdSdGFXND0ifX0.DE2iRA.ig5KSlnmsDH4uhDpmsFRPupB5Vw' -s '.{y]tR&sp&77RdO~u3@XAh#TalD@Oh~yOF_51H(QV};K|ghT^d' {u'username': 'admin', u'number': '326410031505'}
Starred by 774 users
Forked by 93 users
Languages   Python 88.9% | Shell 11.1%
🌐
GitHub
github.com › eric1234 › werkzeug-cookie-prefix-poc › blob › master › README.md
werkzeug-cookie-prefix-poc/README.md at master · eric1234/werkzeug-cookie-prefix-poc
I decided to see what other platforms had this same or similar issue. Although Flask does not do URL decoding as with the Ruby platform it does unquote both the cookie name and value.
Author   eric1234
🌐
Beautiful Soup
tedboy.github.io › flask › generated › werkzeug.dump_cookie.html
werkzeug.dump_cookie — Flask API
werkzeug.dump_cookie · View page source · werkzeug.dump_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False, charset='utf-8', sync_expires=True)[source]¶ · Creates a new Set-Cookie header without the Set-Cookie prefix The parameters are the same as in the cookie Morsel object in the Python standard library but it accepts unicode data, too.
🌐
GitHub
github.com › eric1234 › werkzeug-cookie-prefix-poc
GitHub - eric1234/werkzeug-cookie-prefix-poc: POC for minor werkzeug cookie prefix security issue
I decided to see what other platforms had this same or similar issue. Although Flask does not do URL decoding as with the Ruby platform it does unquote both the cookie name and value.
Author   eric1234
Find elsewhere
🌐
GitHub
github.com › pallets › werkzeug › issues › 1060
test client adds quotes around cookie value · Issue #1060 · pallets/werkzeug
January 18, 2017 - I would expect the same value that I set with set_cookie to be returned when using the cookie.value property. But perhaps I am missing something here? I have added a test case that demonstrates the issue. import unittest from unittest import TestCase from werkzeug.test import Client from werkzeug.testapp import test_app from werkzeug.wrappers import BaseResponse class WerkzeugCookieTest(TestCase): def get_cookie_from_client(self, cookie_name, client): for cookie in client.cookie_jar: if cookie.name == cookie_name: return cookie return None def test_cookie(self): c = Client(test_app, BaseRespon
Author   pallets
🌐
GitHub
gist.github.com › ergoithz › 8a19b0395373bf31297281a3465ead52
werkzeug parse_set_cookie · GitHub
werkzeug parse_set_cookie · Raw · parse_set_cookie.py · This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters ·
🌐
GitHub
github.com › miguelgrinberg › Flask-SocketIO › issues › 1982
Test cookie handling API changed in Werkzeug 2.3 · Issue #1982 · miguelgrinberg/Flask-SocketIO
April 29, 2023 - [#1060](https://github.com/pallets/werkzeug/issues/1060), [#1680](https://github.com/pallets/werkzeug/issues/1680) - The cookie_jar attribute is deprecated. http.cookiejar is no longer used for storage. - Domain and path matching is used when sending cookies in requests. The domain and path parameters default to localhost and /. - Added a get_cookie method to inspect cookies. - Cookies have decoded_key and decoded_value attributes to match what the app sees rather than the encoded values a client would see.
Author   miguelgrinberg
🌐
Medium
blog.paradoxis.nl › defeating-flasks-session-management-65706ba9d3ce
Baking Flask cookies with your secrets | Paradoxis
July 31, 2021 - Flask-Unsign has three main use-cases: it lets you: Decode, Sign and Unsign (crack) a cookie, with built-in HTTP support, which prevents you from having to open up your browser.
🌐
Beautiful Soup
tedboy.github.io › flask › generated › generated › werkzeug.Response.set_cookie.html
werkzeug.Response.set_cookie — Flask API
The parameters are the same as in the cookie Morsel object in the Python standard library but it accepts unicode data, too.
🌐
Readthedocs
kyoukai.readthedocs.io › en › latest › _modules › werkzeug › wrappers.html
werkzeug.wrappers — Kyoukai 2.2.1 documentation
""" from functools import ... dump_options_header, http_date, \ parse_if_range_header, parse_cookie, dump_cookie, \ parse_range_header, parse_content_range_header, dump_header from werkzeug.urls import url_decode, iri_to_uri, url_join from werkzeug.formparser import FormDataParser, ...
🌐
pytz
pythonhosted.org › Findig › _modules › werkzeug › wrappers.html
werkzeug.wrappers — Findig 0.1.0.dev2 documentation
""" from functools import ... dump_options_header, http_date, \ parse_if_range_header, parse_cookie, dump_cookie, \ parse_range_header, parse_content_range_header, dump_header from werkzeug.urls import url_decode, iri_to_uri, url_join from werkzeug.formparser import FormDataParser, ...
🌐
GitHub
github.com › pallets › werkzeug › issues › 31
Secret key rotation with SecureCookie sessions · Issue #31 · pallets/werkzeug
January 30, 2011 - One of the shortcomings I've found in every existing python web session library is that they don't support secret key rotation. It's one of the best ways I know of to protect against brute force and hash algorithm attacks, so I've been l...
Author   pallets
🌐
Toolsfornerds
toolsfornerds.net › cookie-parser
HTTP Cookie Parser & Decoder | ToolsForNerds
Free HTTP cookie parser and decoder. Convert cookie strings from browser DevTools into JSON. Debug authentication, sessions, and cookie attributes easily.
🌐
HackTricks
book.hacktricks.xyz › home › network services pentesting › pentesting web › flask
Flask - HackTricks
1 week ago - Command line tool to fetch, decode, brute-force and craft session cookies of a Flask application by guessing secret keys.