Unbounded Body Reads Enable Memory and Disk Exhaustion
Security Report: Unbounded Body Reads Enable Memory and Disk Exhaustion
Summary
Hetty reads full HTTP request and response bodies into memory in multiple logging, filtering, interception, and API conversion paths without applying size limits.
Because these bodies may also be stored in the request log database and later re-read through API paths, a large request or response can cause excessive memory consumption, high disk growth, and degraded availability.
The code contains explicit TODO comments acknowledging the absence of read limits.
Severity
Medium to High
The exact severity depends on deployment and who can feed traffic through the proxy or trigger large responses. In environments where untrusted traffic is proxied or where Hetty is exposed to remote users, this becomes a practical denial-of-service risk.
Affected Components
- Request logging
- Response logging
- Interception filter evaluation
- GraphQL serialization of intercepted bodies
Relevant code:
Technical Details
1. Request logging reads full request bodies into memory
In the request logging middleware:
if req.Body != nil {
// TODO: Use io.LimitReader.
var err error
body, err = ioutil.ReadAll(req.Body)
if err != nil {
...
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
clone.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}This reads the entire request body into memory without bounds.
2. Response logging reads full response bodies into memory
In response logging:
if res.Body != nil {
// TODO: Use io.LimitReader.
body, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("reqlog: could not read response body: %w", err)
}
res.Body = io.NopCloser(bytes.NewBuffer(body))
clone.Body = io.NopCloser(bytes.NewBuffer(body))
}The full response body is buffered in memory and then passed to background storage.
3. Stored response bodies are read again without limits
func ParseHTTPResponse(res *http.Response) (ResponseLog, error) {
body, err := io.ReadAll(res.Body)
if err != nil {
return ResponseLog{}, fmt.Errorf("reqlog: could not read body: %w", err)
}
return ResponseLog{
Proto: res.Proto,
StatusCode: res.StatusCode,
Status: res.Status,
Header: res.Header,
Body: body,
}, nil
}Source: dstotijn/hetty