How to fix Unrestricted Resource Consumption
in Plug
Executive Summary
Unrestricted resource consumption in Elixir/Plug apps is a fast track to a DoS. If you fail to cap body sizes, an attacker can flood your BEAM node with massive payloads, triggering the OOM killer or saturating process memory. In the Erlang VM, while processes are isolated, a single massive binary allocation can still compromise node stability. You must enforce strict limits at the parser level to drop malicious streams before they exhaust your heap.
The Vulnerable Pattern
defmodule Router do use Plug.RouterVULNERABLE: No length limit specified.
Plug.Parsers defaults to 8MB, but many devs override or ignore it,
and custom implementations often read the whole stream blindly.
plug Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: [”/”], json_decoder: Jason
plug :match plug :dispatch
post “/upload” do # Manual body reading without limits is even worse {:ok, body, conn} = Plug.Conn.read_body(conn) send_resp(conn, 200, “Data received”) end end
The Secure Implementation
The vulnerability stems from trusting the client's Content-Length header or lack thereof. In the vulnerable example, `Plug.Parsers` or a raw `read_body/1` call can be exploited by sending a multi-gigabyte stream that the server attempts to load into memory. The fix involves two layers: 1. Setting the `:length` option in `Plug.Parsers` to globally reject oversized payloads with a 413 Request Entity Too Large error. 2. When using `Plug.Conn.read_body/2` directly, always provide a `:length` (maximum total bytes) and `:read_length` (bytes to read per iteration). This ensures the BEAM process handling the request is terminated or the request is rejected before it can impact the system's global memory pool.
defmodule Router do use Plug.RouterSECURE: Explicitly define the :length option (in bytes).
This prevents the parser from consuming more than 5MB.
plug Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: [”/”], json_decoder: Jason, length: 5_000_000
plug :match plug :dispatch
post “/upload” do # SECURE: Enforce length and read_length during manual reads. case Plug.Conn.read_body(conn, length: 5_000_000, read_length: 1_000_000) do {:ok, body, conn} -> send_resp(conn, 200, “Safe”) {:more, _partial_body, conn} -> send_resp(conn, 413, “Payload Too Large”) {:error, _reason} -> send_resp(conn, 400, “Bad Request”) end end end
Your Plug API
might be exposed to Unrestricted Resource Consumption
74% of Plug apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.