# Elysia FormData DoS PoC Elysia v1.4.28 is vulnerable to denial-of-service attacks due to CPU exhaustion in the form data normalization code. This report includes a proof-of-concept that demonstrates the issue by sending a form data payload with 500 000 distinct keys (< 2 megabytes) to a minimal Elysia server, causing the server to become unresponsive while it processes the request. After the proof-of-concept this report goes over the root cause of the issue. ## Proof-of-Concept Requirements: * Bun * curl ### Run the server In one terminal, from the repo root: ```sh bun install bun run server.ts ``` The server listens on `http://localhost:3000`. ### Send a request In a second terminal, from the repo root: ```sh node send-formdata.mts ``` While the request is being processed, run `curl localhost:3000` and observe that the response is stalled. ## Root Cause The issue stems from the form data normalization code that appears in a couple of places in the codebase. One example is here: https://github.com/elysiajs/elysia/blob/56310be9617b826f862c985eae95ae823d95f097/src/dynamic-handle.ts#L349-L353 The time complexity of a `FormData` method `.getAll` call is directly relative to the _total number of key-value pairs in the form data_. The total amount of work the for loop has to do grows quadratically, so doubling the number of unique key-value pairs quadruples the amount of work. In the above PoC, each `.getAll` call scans through all of the 500 000 key-value pairs in the form data. Because there are 500 000 unique keys in the form data, there are `.getAll` calls, so in total the form data normalizer has to scan 500 000 x 500 000 key-value pairs. The number 500 000 used by the PoC is chosen arbitrarily just to make the issue very visible. ## Remediation It turns out that almost every `FormData` method (`.get`, `.getAll`, `.has`, `.delete` and `.set`) suffers from the linear scan problem due to how every major server-side JavaScript runtime I have looked at implements `FormData`. I have reported this issue to some of them. Therefore the recommended remediation here is to loop over all key-value pairs, e.g. `for (const [key, value] of formData)`, and collect them to an object before normalization. ## Additional Notes There is also one additional point that is not the root cause in itself, but allows squeezing key-value pairs into more compact request bodies: Sending a Content-Type header value like `"multipart/form-data;(, application/x-www-form-urlencoded"` makes the request handler process the request as a `multipart/form-data` request. However, the subsequent `Request.formData` call discards the `multipart/form-data;(` part because `(` is not a valid token character, and uses the next token, `application/x-www-form-urlencoded`, interpreting the body as urlencoded.