Skip to content

Instantly share code, notes, and snippets.

@maelvls
Last active September 17, 2021 15:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maelvls/0e9f8ad464821c29f6bad553571d9f2e to your computer and use it in GitHub Desktop.
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 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