From 251e16d6855812ee8696c5dce7ed11a8e8230950 Mon Sep 17 00:00:00 2001 From: Raphael Eidus Date: Wed, 7 Jul 2021 20:15:58 -0400 Subject: [PATCH] feat: Add optional feature to decompress encoded request body #577 --- README.md | 4 ++++ httpbin/helpers.py | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/README.md b/README.md index 8148d684..2fe93934 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ docker pull kennethreitz/httpbin docker run -p 80:80 kennethreitz/httpbin ``` +### Optional Env Flags + +`UNSAFE_BODY_DECOMPRESSION=1` enable decompressing of compressed request bodies. With this option enabled your instance is vulnerable to [Zip Bombing](https://en.wikipedia.org/wiki/Zip_bomb). + See http://httpbin.org for more information. ## Officially Deployed at: diff --git a/httpbin/helpers.py b/httpbin/helpers.py index b29e1835..412a1322 100644 --- a/httpbin/helpers.py +++ b/httpbin/helpers.py @@ -12,6 +12,9 @@ import re import time import os +import gzip as gzip2 +import brotli as _brotli +import zlib from hashlib import md5, sha256, sha512 from werkzeug.http import parse_authorization_header from werkzeug.datastructures import WWWAuthenticate @@ -81,6 +84,8 @@ YOU SHOULDN'T BE HERE """ +UNSAFE_BODY_DECOMPRESSION = bool(os.environ.get("UNSAFE_BODY_DECOMPRESSION")) + def json_safe(string, content_type='application/octet-stream'): """Returns JSON-safe version of `string`. @@ -168,6 +173,17 @@ def get_url(request): return urlunparse(url) +def get_content_encoding(): + """Returns the clean content encoding""" + raw_encoding = request.headers.get('Content-Encoding') or "" + return raw_encoding.split('/')[-1].lower() + + +def should_decompress(data): + """Checks if this request body should be decompressed""" + return UNSAFE_BODY_DECOMPRESSION and get_content_encoding() in ['gzip', 'brotli', 'deflate'] and data + + def get_dict(*keys, **extras): """Returns request dict of given keys.""" @@ -175,6 +191,17 @@ def get_dict(*keys, **extras): assert all(map(_keys.__contains__, keys)) data = request.data + if should_decompress(data): + try: + encoding = get_content_encoding() + if(encoding == 'gzip'): + data = gzip2.decompress(data) + elif(encoding == 'brotli'): + data = _brotli.decompress(data) + elif(encoding == 'deflate'): + data = zlib.decompress(data) + except Exception as err: + data = str(err).encode('utf-8') form = semiflatten(request.form) try: