Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add optional feature to decompress encoded request body #648

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
27 changes: 27 additions & 0 deletions httpbin/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -168,13 +173,35 @@ 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."""

_keys = ('url', 'args', 'form', 'data', 'origin', 'headers', 'files', 'json', 'method')

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:
Expand Down