HPACK Bomb
A decompression-bomb flaw in the HTTP/2 HPACK header codec that lets an unauthenticated attacker exhaust server memory and take the process down.
Understanding the Threat
Discovered: August 3, 2016 (Public disclosure)
Researcher: Imperva Defense Center, as part of its HTTP/2 protocol analysis
HPACK Bomb is a denial-of-service flaw in hpack, the Python implementation of HTTP/2's HPACK header-compression format (RFC 7541). It is a classic decompression bomb: a tiny compressed payload that expands into an enormous amount of data once the server unpacks it.
Because HTTP/2 negotiates compression before any request body or authentication, the attack is unauthenticated. A mere 16 kB header block can decompress to 64 MB — a 4,096:1 ratio — and a handful of blocks is enough to force the target to allocate gigabytes and crash.
🚨 Why This Matters
- 4,096:1 ratio — 16 kB in, 64 MB out, per header block
- Unauthenticated — Triggered during HTTP/2 header exchange, before any auth
- Cheap to send — A few crafted blocks exhaust gigabytes of RAM
- Protocol-level — Any service decoding attacker HPACK is exposed
- No exploit needed — The "payload" is just well-formed header references
Am I Vulnerable?
If your service decodes HTTP/2 headers using the Python hpack library below version 2.3.0 — or the hyper client below 0.6.0, which bundled a vulnerable copy — an attacker can crash it. Check your installed version:
python3 -c "import hpack; print(hpack.__version__)"You may pull in hpack indirectly through HTTP/2-capable libraries such as hyper, h2, or anything built on them. Run pip show hpack and audit your dependency tree — the vulnerable copy is often a transitive dependency you never installed directly.
Affected Versions
Every release of the HPACK codec before 2.3.0 is affected, along with any client that bundled it.
| Package | Affected Versions | Fixed In |
|---|---|---|
| hpack (Python) | 1.0.0 – 2.2.0 | 2.3.0 |
| hyper (Python) | < 0.6.0 | 0.6.0 |
Technical Details
The attack abuses three properties of HPACK working together:
- The dynamic table — HPACK keeps a per-connection table of previously seen header fields. Each entry can be referenced by a single-byte index in later requests.
- Index references are tiny — One byte on the wire can expand into a full, arbitrarily large header field on decode.
- No output cap — Before 2.3.0 the decoder placed no ceiling on the total decompressed size of a header block.
The attacker inserts a single header field sized to exactly fill the dynamic header table, then sends a header block consisting of nothing but repeated references to that one entry. Each one-byte reference re-expands the maximum-size field. A roughly 16 kB block of these references decompresses to about 64 MB. Send a few such blocks and the process is forced to allocate gigabytes of memory — there is no limit to stop it, so the process is OOM-killed.
- HTTP/2 API servers — Any endpoint terminating HTTP/2 with the affected codec
- Reverse proxies & gateways — Python-based edge services decoding client headers
- HTTP/2 clients — A malicious server can bomb a vulnerable client just as easily
- Microservice meshes — One compromised hop can DoS downstream HTTP/2 peers
Verify Your Version
pip show hpack | grep -i version
# Vulnerable if below 2.3.0Reference: hpack >= 2.3.0 enforces SETTINGS_MAX_HEADER_LIST_SIZE
How to Fix
1Upgrade the Library
Version 2.3.0 added support for SETTINGS_MAX_HEADER_LIST_SIZE, which caps the maximum decompressed size of a header block (default 64 kB, user-configurable). Upgrade to 2.3.0 or later.
pip install --upgrade 'hpack>=2.3.0'pip install --upgrade 'hyper>=0.6.0'2Set an Explicit Header-List Limit
After upgrading, set a header-list size that matches what your application legitimately needs rather than relying on the default. Anything beyond it is rejected before decompression runs away:
from hpack import Decoder
decoder = Decoder()
# Reject any header block that decompresses past 64 kB
decoder.max_header_list_size = 64 * 1024What breaks? Nothing, for normal traffic. Legitimate header blocks are kilobytes at most. The limit only trips on pathological inputs — i.e. the attack.
3Defense in Depth at the Edge
If you cannot upgrade every service immediately, terminate HTTP/2 at a hardened reverse proxy that enforces its own header limits, and cap per-connection memory for the application process:
# Bound the worst case so a bomb can't take the host down
[Service]
MemoryMax=512M
MemoryHigh=384MCritical: Restart Workers
Upgrading the package is not enough — long-running workers keep the old code loaded in memory. Restart every process that imports hpack after upgrading.