[GH-ISSUE #23] httpupgrade inbound: no read deadline or header size limit enables pre-auth DoS #2

Open
opened 2026-10-13 21:10:43 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @FrogRocky on GitHub (Aug 17, 2026).
Original GitHub issue: https://github.com/amnezia-vpn/amnezia-xray-core/issues/23

Summary

The httpupgrade inbound handler reads the upgrade request with no read deadline and no header size limit, allowing unauthenticated resource exhaustion on any open httpupgrade listener.

Affected code: transport/internet/httpupgrade/hub.go (upgrade(), the per-connection goroutine):

connReader := bufio.NewReader(conn)
req, err := http.ReadRequest(connReader)

Impact

Two pre-authentication DoS vectors, before any proxy protocol or credential is seen:

  1. Slowloris: an attacker opens N TCP connections and sends one byte of the request line every few minutes. Each connection pins a goroutine plus a ~4 KB bufio buffer forever — no timeout ever fires.
  2. Unbounded header memory: http.ReadRequest (unlike http.Server) does not apply MaxHeaderBytes; textproto grows the line buffer without bound. A single connection sending GET / HTTP/1.1\r\nX-P: followed by a multi-GB header value without a terminator buffers the whole thing in memory → OOM.

Sibling transports already handle this — the websocket hub sets ReadHeaderTimeout: 4s and MaxHeaderBytes: 8192, and the splithttp hub sets ReadHeaderTimeout: 4s. httpupgrade has neither.

Suggested fix

In the connection goroutine, before http.ReadRequest:

  • call conn.SetReadDeadline(time.Now().Add(4 * time.Second)) (and clear it after the 101 response is written), and
  • cap the buffered reader / reject requests whose header section exceeds ~8 KB, mirroring websocket/hub.go.
Originally created by @FrogRocky on GitHub (Aug 17, 2026). Original GitHub issue: https://github.com/amnezia-vpn/amnezia-xray-core/issues/23 ## Summary The httpupgrade inbound handler reads the upgrade request with no read deadline and no header size limit, allowing unauthenticated resource exhaustion on any open httpupgrade listener. **Affected code:** `transport/internet/httpupgrade/hub.go` (`upgrade()`, the per-connection goroutine): ```go connReader := bufio.NewReader(conn) req, err := http.ReadRequest(connReader) ``` ## Impact Two pre-authentication DoS vectors, before any proxy protocol or credential is seen: 1. **Slowloris:** an attacker opens N TCP connections and sends one byte of the request line every few minutes. Each connection pins a goroutine plus a ~4 KB bufio buffer forever — no timeout ever fires. 2. **Unbounded header memory:** `http.ReadRequest` (unlike `http.Server`) does not apply `MaxHeaderBytes`; `textproto` grows the line buffer without bound. A single connection sending `GET / HTTP/1.1\r\nX-P: ` followed by a multi-GB header value without a terminator buffers the whole thing in memory → OOM. Sibling transports already handle this — the websocket hub sets `ReadHeaderTimeout: 4s` and `MaxHeaderBytes: 8192`, and the splithttp hub sets `ReadHeaderTimeout: 4s`. httpupgrade has neither. ## Suggested fix In the connection goroutine, before `http.ReadRequest`: - call `conn.SetReadDeadline(time.Now().Add(4 * time.Second))` (and clear it after the 101 response is written), and - cap the buffered reader / reject requests whose header section exceeds ~8 KB, mirroring `websocket/hub.go`.
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-xray-core#2
No description provided.