Last active
September 17, 2021 15:01
-
-
Save maelvls/0e9f8ad464821c29f6bad553571d9f2e to your computer and use it in GitHub Desktop.
A mitmproxy pretty-printer for displaying the rfc8555 application/jose+json content type.
This file contains hidden or 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
""" | |
This custom pretty-printer for mitmproxy will decode the base64url-encoded | |
'payload' and 'protected' fields. | |
This pretty-printer is useful for understanding the POST requests made to | |
an ACME server, since these requests are made using JWS as JSON, which, | |
contrary to JWT tokens that are very easy to decode, aren't as common. | |
The whole JWS as JSON and application/jose+json are detailed in | |
https://tools.ietf.org/html/rfc8555. | |
To use this pretty-printer, run mitmproxy with | |
mitmproxy -s josejson.py | |
""" | |
from mitmproxy import contentviews, ctx | |
import json | |
import base64 | |
import typing | |
import re | |
from mitmproxy.contentviews import base | |
def format_json(data: typing.Any) -> typing.Iterator[base.TViewLine]: | |
encoder = json.JSONEncoder(indent=4, sort_keys=True, ensure_ascii=False) | |
current_line: base.TViewLine = [] | |
for chunk in encoder.iterencode(data): | |
if "\n" in chunk: | |
rest_of_last_line, chunk = chunk.split("\n", maxsplit=1) | |
# rest_of_last_line is a delimiter such as , or [ | |
current_line.append(("text", rest_of_last_line)) | |
yield current_line | |
current_line = [] | |
if re.match(r'\s*"', chunk): | |
current_line.append(("json_string", chunk)) | |
elif re.match(r"\s*\d", chunk): | |
current_line.append(("json_number", chunk)) | |
elif re.match(r"\s*(true|null|false)", chunk): | |
current_line.append(("json_boolean", chunk)) | |
else: | |
current_line.append(("text", chunk)) | |
yield current_line | |
class ViewJoseJson(contentviews.View): | |
name = "jose+json" | |
content_types = ["application/jose+json"] | |
def __call__(self, data: bytes, **metadata) -> contentviews.TViewResult: | |
data = json.loads(data.decode("utf-8")) | |
ctx.log.info("ok") | |
return ( | |
"protected & payload base64-decoded", | |
format_json(josejson(data)), | |
) | |
def josejson(data: typing.Any) -> typing.Any: | |
if len(data["protected"]) == 0: | |
data["protected"] = "e30" # '{}' in base64 | |
# https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding | |
data["protected"] += "===" | |
try: | |
data["protected"] = json.loads(base64.urlsafe_b64decode(data["protected"])) | |
except Exception as e: | |
ctx.log.info(e) | |
if len(data["payload"]) == 0: | |
data["payload"] = "e30" # '{}' in base64 | |
# https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding | |
data["payload"] += "===" | |
try: | |
data["payload"] = json.loads(base64.urlsafe_b64decode(data["payload"])) | |
except Exception as e: | |
ctx.log.info(e) | |
return data | |
view = ViewJoseJson() | |
def load(l): | |
contentviews.add(view) | |
def done(): | |
contentviews.remove(view) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment