vshakitskiy/ewe · Security
Permissive List of Allowed Inputs in ewe
- GHSA ID
- GHSA-9w88-79f8-m3vp
- CVE ID
- CVE-2026-32881
- Severity
- medium
- Published
- Updated
- CVSS
- 5.3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
- CVSS v3
- 5.3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Description
Summary
ewe's chunked transfer encoding trailer handling merges declared trailer fields into req.headers after body parsing, but the denylist only blocks 9 header names. Security-sensitive headers like authorization, cookie, and x-forwarded-for can be injected or overwritten by a malicious client via trailers, potentially bypassing authentication or spoofing proxy-trust headers.
Impact
When ewe.read_body processes a chunked request with a Trailer header, it calls handle_trailers (ewe/internal/http1.gleam:493), which merges declared trailer fields into req.headers via request.set_header (line 517). The is_forbidden_trailer denylist (line 534) only blocks 9 header names: transfer-encoding, content-length, host, cache-control, expect, max-forwards, pragma, range, and te.
Security-sensitive headers are not blocked, including:
authorization— attacker can inject or overwrite Bearer tokenscookie/set-cookie— attacker can inject session cookiesproxy-authorization— attacker can inject proxy credentialsx-forwarded-for,x-forwarded-host,x-forwarded-proto— attacker can spoof proxy-trust headersx-real-ip— attacker can spoof client IP
A malicious client can inject these headers by declaring them in the Trailer request header and including them after the final 0\r\n chunk. If the header already exists (e.g., set by a reverse proxy), request.set_header overwrites it. Any application logic that reads these headers after calling ewe.read_body — such as authentication middleware, IP-based rate limiting, or session validation — will see the attacker-controlled values.
Proof of Concept
Inject an authorization header that didn't exist:
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nTransfer-Encoding: chunked\r\nTrailer: authorization\r\n\r\n4\r\ntest\r\n0\r\nauthorization: Bearer injected-token\r\n\r\n' | nc -w 2 localhost 8080
Overwrite a legitimate authorization header set by a proxy:
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nAuthorization: Bearer legitimate-token\r\nTransfer-Encoding: chunked\r\nTrailer: authorization\r\n\r\n4\r\ntest\r\n0\r\nauthorization: Bearer evil-token\r\n\r\n' | nc -w 2 localhost 8080
Inject x-forwarded-for to spoof client IP:
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nTransfer-Encoding: chunked\r\nTrailer: x-forwarded-for\r\n\r\n4\r\ntest\r\n0\r\nx-forwarded-for: 10.0.0.1\r\n\r\n' | nc -w 2 localhost 8080
Patches
- Expand the denylist in
is_forbidden_trailerto includeauthorization,cookie,set-cookie,proxy-authorization,x-forwarded-for,x-forwarded-host,x-forwarded-proto,x-real-ip, and other security-sensitive headers. - Alternatively, switch to an allowlist model that only permits explicitly safe trailer field names.
CWEs
- CWE-183 · Permissive List of Allowed Inputs
References
- https://github.com/vshakitskiy/ewe/security/advisories/GHSA-9w88-79f8-m3vp ↗
- https://github.com/vshakitskiy/ewe/commit/07dcfd2135fc95f38c17a9d030de3d7efee1ee39 ↗
- https://github.com/vshakitskiy/ewe/commit/94ab6e7bf7293e987ae98b4daa51ea131c2671ba ↗
- https://github.com/vshakitskiy/ewe/releases/tag/v3.0.5 ↗
- https://nvd.nist.gov/vuln/detail/CVE-2026-32881 ↗
- https://github.com/advisories/GHSA-9w88-79f8-m3vp ↗