Unbounded HTTP Reads

Why trusting `readAll()` on a network socket is a fast track to an Out of Memory crash.

The idea

When your server receives an HTTP request, or when it calls a third-party API and downloads the response, the data arrives as a stream of bytes over the network. It is very tempting to write a single line of code that says "read all bytes into memory as a string." But what if an attacker (or a misconfigured API) sends a 10 Gigabyte payload? Your server will try to allocate 10GB of RAM to hold that string, immediately running out of memory (OOM) and crashing. This is called an Unbounded Read vulnerability.

Step 1: Normal. We use readAll() for a small 5KB JSON payload. It fits in RAM perfectly.

How it works (Bounded Reads & Streaming)

To fix this, you must bound your reads. Tell the OS: "Read the body, but stop immediately if it exceeds 1 Megabyte." Alternatively, if you actually expect large files, you must stream them. Instead of reading the whole file into RAM, you read it in small chunks (e.g., 64KB at a time), process that chunk, or write it directly to a hard drive/S3, and then discard it from RAM.

// BAD: Unbounded Read (Go Example)
// If resp.Body is an endless stream of garbage, we OOM and crash.
bodyBytes, err := io.ReadAll(resp.Body) 

// GOOD: Bounded Read (Limit to 1MB max)
// io.LimitReader acts as a safety valve.
safeReader := io.LimitReader(resp.Body, 1 * 1024 * 1024) 
bodyBytes, err := io.ReadAll(safeReader)
if err != nil {
    return fmt.Errorf("Payload too large!")
}

// GOOD: Streaming (For large files)
// Copies directly from Network to Disk in tiny memory chunks.
io.Copy(diskFile, resp.Body)

Cost

Setting a hard bound means legitimate requests might occasionally fail if they exceed the limit. You must carefully tune the maximum size limit based on your expected payloads. For JSON APIs, a 1MB to 5MB limit is usually very generous. For file uploads, you should rely entirely on streaming, bypassing memory buffers altogether.

Watch out for