Back to Blog
high SEVERITY6 min read

How CR/LF Injection happens in Node.js http-proxy-middleware and how to fix it

A high-severity CRLF injection vulnerability (CVE-2026-55603) was discovered in http-proxy-middleware versions prior to 3.0.7, allowing attackers to inject carriage return and line feed characters into proxied requests, potentially compromising data integrity. The fix upgrades the dependency from version 3.0.5 to 3.0.7, which adds proper sanitization of CR/LF characters in user-controlled input before forwarding requests to backend services.

O
By Orbis AppSec
Published August 21, 2026Reviewed August 21, 2026

Answer Summary

CVE-2026-55603 is a high-severity CRLF (Carriage Return/Line Feed) injection vulnerability in the Node.js package http-proxy-middleware (versions before 3.0.7 and 4.1.1), classified under CWE-93 (Improper Neutralization of CRLF Sequences). Attackers can inject \r\n characters into proxied HTTP headers or URLs, compromising data integrity. The fix is to upgrade http-proxy-middleware to version 3.0.7 (or 4.1.1 for the v4 branch), which sanitizes untrusted input to strip or reject CR/LF sequences before proxying requests.

Vulnerability at a Glance

cweCWE-93
fixUpgrade http-proxy-middleware from 3.0.5 to 3.0.7
riskData integrity compromise via header/response splitting
languageJavaScript (Node.js)
root causeUnsanitized user input containing CR/LF characters passed through proxy middleware to backend servers
vulnerabilityCRLF Injection (HTTP Header Injection)

Introduction

In a Node.js application using Express 5.x with http-proxy-middleware for request proxying, Trivy flagged a high-severity vulnerability in package-lock.json: the pinned dependency http-proxy-middleware@3.0.5 was susceptible to CVE-2026-55603, a data integrity compromise via CR/LF injection. This vulnerability allows an attacker to inject carriage return (\r) and line feed (\n) characters into proxied HTTP requests, potentially splitting headers, injecting malicious content, or poisoning caches.

The package.json declared the dependency as "http-proxy-middleware": "^3.0.5", and the lockfile resolved it to exactly version 3.0.5—a version that did not sanitize CRLF sequences in user-controlled input before forwarding requests to upstream servers. For any developer running a reverse proxy or API gateway with this middleware, this represents a direct path from user input to protocol-level manipulation.

The Vulnerability Explained

What is CRLF Injection?

HTTP uses \r\n (CRLF) sequences to separate headers from each other and from the body. When user-controlled data—such as URL paths, query parameters, or custom headers—is incorporated into a proxied HTTP request without stripping these characters, an attacker can:

  1. Inject arbitrary HTTP headers into the proxied request
  2. Split the HTTP response, creating a second fabricated response
  3. Poison web caches with malicious content
  4. Bypass security controls that rely on header integrity

How This Affected http-proxy-middleware 3.0.5

The http-proxy-middleware package acts as a bridge between an Express (or similar) application and a backend target server. When configured, it takes incoming requests and forwards them to the target. In version 3.0.5, the middleware did not adequately validate or sanitize input that could contain CRLF sequences before constructing the outbound proxy request.

Consider a typical setup:

const { createProxyMiddleware } = require('http-proxy-middleware');
const express = require('express');

const app = express();
app.use('/api', createProxyMiddleware({
  target: 'http://backend-service:3000',
  changeOrigin: true,
}));

An attacker could craft a request like:

GET /api/resource%0d%0aX-Injected-Header:%20malicious-value HTTP/1.1
Host: vulnerable-app.com

When http-proxy-middleware@3.0.5 forwarded this request, the %0d%0a (URL-decoded \r\n) could be interpreted as a header boundary, injecting X-Injected-Header: malicious-value into the proxied request sent to the backend. This compromises the integrity of the communication between the proxy and the upstream server.

Real-World Attack Scenario

Imagine this application proxies authentication requests to a backend identity service. An attacker could inject headers like:

GET /api/auth%0d%0aX-Forwarded-For:%20127.0.0.1%0d%0aX-Admin:%20true HTTP/1.1

This could cause the backend to see X-Forwarded-For: 127.0.0.1 and X-Admin: true as legitimate headers from the proxy, potentially bypassing IP-based access controls or elevating privileges.

The Fix

The fix upgrades http-proxy-middleware from version 3.0.5 to 3.0.7, which introduces internal sanitization of CRLF sequences in request data before forwarding.

Before (Vulnerable)

In package.json:

{
  "dependencies": {
    "express": "^5.1.0",
    "http-proxy-middleware": "^3.0.5"
  }
}

In package-lock.json:

"node_modules/http-proxy-middleware": {
  "version": "3.0.5",
  "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz",
  "integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg=="
}

After (Fixed)

In package.json:

{
  "dependencies": {
    "express": "^5.1.0",
    "http-proxy-middleware": "^3.0.7"
  }
}

In package-lock.json:

"node_modules/http-proxy-middleware": {
  "version": "3.0.7",
  "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.7.tgz",
  "integrity": "sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw=="
}

What Changed Internally

Version 3.0.7 of http-proxy-middleware adds validation logic that strips or rejects \r and \n characters from user-influenced data before it is incorporated into the outbound proxy request. This ensures that even if an attacker supplies CRLF sequences in URLs, query strings, or headers, they cannot manipulate the HTTP protocol structure of the proxied request.

The engine requirement also shifted subtly from ^14.15.0 to ^14.18.0, indicating the fix leverages Node.js APIs available in 14.18+ for safer header handling.

Why Both Files Changed

  • package.json: Updates the declared dependency range to require at minimum version 3.0.7
  • package-lock.json: Pins the exact resolved version, integrity hash, and registry URL to ensure reproducible builds with the patched version

Prevention & Best Practices

1. Keep Dependencies Updated

Use automated dependency scanning tools (Trivy, Snyk, npm audit) in your CI/CD pipeline to catch vulnerable versions early:

npm audit
# or
trivy fs --scanners vuln .

2. Validate Input at the Edge

Even with patched middleware, defense-in-depth means validating input before it reaches the proxy:

app.use('/api', (req, res, next) => {
  // Reject requests with CRLF in the URL
  if (/[\r\n]/.test(decodeURIComponent(req.originalUrl))) {
    return res.status(400).send('Invalid request');
  }
  next();
});

3. Use Allowlists for Headers

When configuring proxy middleware, explicitly define which headers are forwarded rather than passing everything through:

createProxyMiddleware({
  target: 'http://backend:3000',
  headers: { 'X-Custom': 'safe-value' },
  onProxyReq: (proxyReq, req) => {
    // Remove potentially dangerous headers
    proxyReq.removeHeader('x-forwarded-for');
  }
});

4. Pin Dependencies with Lockfiles

Always commit your package-lock.json and use npm ci in production builds to ensure you get exactly the versions you tested.

5. Monitor for CVEs in Transitive Dependencies

http-proxy-middleware depends on http-proxy, micromatch, and other packages. A vulnerability in any transitive dependency can affect your application.

Key Takeaways

  • http-proxy-middleware 3.0.5 did not sanitize CRLF characters in user-controlled input before constructing proxied HTTP requests, enabling header injection attacks
  • Upgrading from 3.0.5 to 3.0.7 is the minimum fix—the ^3.0.7 semver range in package.json ensures future patch versions are also accepted
  • CRLF injection in proxy middleware is particularly dangerous because it operates at the protocol level between services, often behind WAFs and other perimeter defenses
  • The lockfile integrity hash change (sha512-GLZZm...sha512-iwbQ...) confirms the actual binary content of the package changed, not just metadata
  • Defense-in-depth matters: even with the patched middleware, adding input validation at the application layer provides an additional safety net against similar future vulnerabilities

How Orbis AppSec Detected This

  • Source: User-controlled HTTP request data (URL path, query parameters, headers) entering the Express application
  • Sink: http-proxy-middleware@3.0.5 proxy forwarding logic that constructs outbound HTTP requests to backend services without CRLF sanitization
  • Missing control: No validation or stripping of \r\n (CR/LF) sequences in user input before incorporation into proxied HTTP requests
  • CWE: CWE-93 (Improper Neutralization of CRLF Sequences in HTTP Headers)
  • Fix: Upgraded http-proxy-middleware from 3.0.5 to 3.0.7 in both package.json and package-lock.json, which adds internal CRLF sanitization before proxying requests

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

CVE-2026-55603 demonstrates how a seemingly simple oversight—failing to strip two characters (\r and \n) from user input—can escalate into a high-severity vulnerability capable of compromising data integrity between services. For Node.js applications using http-proxy-middleware, the fix is straightforward: upgrade to version 3.0.7 or later. But the broader lesson is that any component sitting on the boundary between user input and HTTP protocol construction must be treated with extreme care. Automated dependency scanning, combined with defense-in-depth input validation, ensures that vulnerabilities like this are caught and remediated before they reach production.

References

Frequently Asked Questions

What is CRLF injection?

CRLF injection occurs when an attacker inserts carriage return (\r) and line feed (\n) characters into input that is used in HTTP headers or responses, allowing them to inject arbitrary headers, split responses, or manipulate the HTTP protocol structure.

How do you prevent CRLF injection in Node.js?

Prevent CRLF injection by validating and sanitizing all user-controlled input before incorporating it into HTTP headers or URLs, rejecting or stripping any \r\n sequences, and keeping proxy middleware dependencies updated to patched versions.

What CWE is CRLF injection?

CRLF injection is classified as CWE-93 (Improper Neutralization of CRLF Sequences in HTTP Headers, also known as HTTP Response Splitting).

Is URL encoding enough to prevent CRLF injection?

URL encoding alone is not sufficient because some middleware or backends may decode the input before processing it. Proper prevention requires explicit rejection or stripping of CR/LF characters after all decoding steps, which is what http-proxy-middleware 3.0.7 implements.

Can static analysis detect CRLF injection?

Yes, static analysis tools like Trivy, Semgrep, and Snyk can detect known vulnerable versions of packages like http-proxy-middleware and flag CRLF injection patterns in custom code where user input flows into HTTP headers without sanitization.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #27

Related Articles

high

How Client-Side Denial of Service happens in Node.js FTP clients and how to fix it

CVE-2026-44240 is a client-side Denial of Service vulnerability in the `basic-ftp` Node.js package (versions prior to 5.3.1) caused by improper handling of unterminated multiline FTP server responses. An attacker controlling an FTP server—or capable of intercepting FTP traffic—could send a malformed response that causes the client to hang indefinitely. Upgrading `basic-ftp` to 5.3.1 and adding a package override in `package.json` closes the attack surface entirely.

high

How javascript.express.security.audit.express-check-csurf-middleware-usage.express-check-csurf-middleware-usage happens in Express.js and how to fix it

A publicly accessible Express.js API endpoint in `app/api/cameras.js` was missing CSRF protection, leaving state-changing requests (POST, PUT, DELETE, PATCH) vulnerable to cross-site request forgery attacks. The fix introduces Origin/Referer header validation middleware in `app/index.js` and removes a redundant Express instance from `cameras.js` that bypassed the application's middleware chain.

critical

How Command Injection via Unescaped Line Terminators Happens in Node.js and How to Fix It

A critical command injection vulnerability (CVE-2026-9277) was discovered in the shell-quote npm package version 1.8.3, where unescaped line terminators could allow attackers to execute arbitrary code. This fix upgrades shell-quote to version 1.9.0 using npm overrides to ensure all instances in the dependency tree are patched, eliminating the attack vector across the entire application.

critical

How Distributed Lock Takeover Happens in Node.js and How to Fix It

A critical vulnerability in `redis-lock/server.mjs` allowed any authenticated client to release another client's lock by guessing predictable holder identifiers like process IDs or hostnames. The fix implements cryptographically random `lockId` values that are minted on lock acquisition and validated on release, eliminating the exploit primitive entirely.

high

How Denial of Service via Infinite Loop happens in JavaScript (nanoid) and how to fix it

A high-severity denial of service vulnerability (CVE-2026-67213) was discovered in nanoid versions before 5.1.6 and 3.3.18, where the `customAlphabet` function could enter an infinite loop during random ID generation. The fix upgrades the transitive nanoid dependency from 3.3.16 to 3.3.18 using pnpm overrides, ensuring the vulnerable code path is eliminated from the entire dependency tree including PostCSS.

high

How Information Disclosure via Unstripped Credential Headers Happens in Electron Apps and How to Fix It

A high-severity vulnerability (CVE-2026-54673) in the builder-util-runtime package allowed sensitive credential headers to leak during HTTP redirects in Electron applications. The fix upgrades builder-util-runtime from version 9.5.1 to 9.7.0, which properly strips authentication headers before following redirects to prevent information disclosure.