express (npm)
Registry: npm Weekly Downloads: ~84,000,000 (as of 2026-04-10) Repository: https://github.com/expressjs/express Security Contact: security@expressjs.com / express-security@lists.openjsf.org Disclosure Policy: https://github.com/expressjs/express/security/policy Threat Model: https://github.com/expressjs/security-wg/blob/main/docs/ThreatModel.md Current Status: audit-ingested
Audit History
| Date | Auditor | Scope | Methodology | Findings | Source | |------|---------|-------|-------------|----------|--------| | 2026-04-10 | @travis-burmaster | full-source (core + key deps) | hybrid (manual review + automated) | 14 findings (2 high, 4 medium-high, 2 medium, 6 confirmed safe) | oss-security-kb |
Audit scope: Express 5.2.1 core (2762 lines across 6 files: application.js, express.js, request.js, response.js, utils.js, view.js) plus security-critical dependencies: send 1.1.0, serve-static 2.2.0, qs 6.15.1, cookie 0.7.2, body-parser 2.2.2, proxy-addr 2.0.7, encodeurl 2.0.0, finalhandler 2.1.0. Commit 8e022ed (expressjs/express main branch, 2026-04-10).
Not in scope: Express middleware ecosystem (helmet, cors, csurf, etc.), application-level code, third-party template engines.
Findings
High Severity
H1: Open Redirect in res.redirect() -- No URL Scheme Validation
Severity: High (CWE-601) In-scope per threat model: Yes -- untrusted network data causing unvalidated redirect
res.redirect() performs zero validation on the redirect URL. encodeUrl() only encodes special characters -- schemes like javascript:, data:, and protocol-relative URLs (//evil.com) pass through intact.
Code: lib/response.js:812-864 (redirect), lib/response.js:795 (location)
Exploit:
// Vulnerable pattern (extremely common in login flows)
app.get('/login', (req, res) => {
res.redirect(req.query.next || '/');
});
// Attack: /login?next=javascript:alert(document.cookie)
// Attack: /login?next=//evil.com/phish
// Attack: /login?next=data:text/html,<script>alert(1)</script>
Impact: Credential phishing, XSS via javascript: scheme, token theft. Affects every Express app using user-controlled redirects.
H2: req.hostname Injection -- Null Bytes, Path Injection, No Sanitization
Severity: High (CWE-113 / CWE-644) In-scope per threat model: Yes -- untrusted network data corrupting header-derived values
req.hostname reads X-Forwarded-Host (trust proxy enabled) or Host header with zero validation. Only port stripping via colon-split is applied.
Code: lib/request.js:418-458
Attack vectors:
- Null byte injection:
evil.com\x00.legit.com-- DNS libraries truncate at null - Path injection:
evil.com/admin-- poisons URL construction - No RFC hostname validation
Impact: Cache poisoning, SSRF, password reset poisoning, OAuth callback manipulation.
Medium-High Severity
MH1: allowPrototypes: true in Extended Query Parser
Express passes allowPrototypes: true to qs in extended mode, allowing queries to shadow Object.prototype keys. Direct __proto__ blocked, but constructor[prototype] creates attacker-controlled nested objects.
Code: lib/utils.js:268
MH2: Silent Parameter Truncation in qs
qs silently drops parameters beyond parameterLimit (default 1000). Attacker pads query with junk to push security-relevant parameters past the limit.
Code: qs/lib/parse.js:67-71
MH3: req.protocol Accepts Arbitrary Values
X-Forwarded-Proto passed through with only trim(). No whitelist. req.secure bypass trivial.
Code: lib/request.js:297-315
MH4: req.subdomains Routing Confusion
Unsanitized req.hostname allows arbitrary subdomain array injection via X-Forwarded-Host.
Code: lib/request.js:383-394
Medium Severity
M1: Stack Traces Leaked by Default
Default env is 'development'. Full stack traces sent to clients unless NODE_ENV=production is set.
Code: finalhandler/index.js:157-171
M2: Symlinks Followed by Default in send
send uses fs.stat() not fs.lstat(). Symlinks inside served root pointing outside it are followed.
Code: send/index.js:605
Out of Scope per Threat Model (Documented for Reference)
- res.sendFile() path traversal without root option -- concatenation bypasses send's protections. Out of scope: "accessing files from any accessible path is not a vulnerability."
- Trust proxy: true enables full IP spoofing -- configuration issue per threat model.
Confirmed Safe
| Area | Status | |------|--------| | Path traversal via encoded chars in send | Defended (layered decode + normalize + regex) | | Double encoding (%252e) | Safe | | Windows backslash traversal | Defended | | Null bytes in static file paths | Blocked | | JSONP callback injection | Sanitized + nosniff | | HTTP header injection via res.set() | Blocked by Node.js runtime | | Range header abuse | Multi-range ignored | | Dotfiles | Default: ignore (404) | | Cookie injection via res.cookie() | Strict validation |
Known Vulnerabilities
| CVE / Issue | Severity | Description | Fixed in | Source | |-------------|----------|-------------|----------|--------| | CVE-2024-45296 | High | ReDoS in path-to-regexp (transitive dep) | path-to-regexp 8.0.0 | osv.dev | | MAL-2026-2419 | Critical | Typosquatting: express-session-js impersonates express-session | N/A (malicious package) | osv.dev |
Supply Chain Notes
The express-* npm namespace is heavily targeted by typosquatting attacks (e.g., MAL-2026-2419). Operators should audit dependencies and use lockfiles.
Recommendations for Developers
- Never pass user input directly to
res.redirect()-- validate against an allowlist - Always set
NODE_ENV=productionin production - Use
res.sendFile()with therootoption for path containment - Set trust proxy to specific addresses, never
true - Validate
req.hostnamebefore using in URL construction or cache keys - Use
'simple'query parser (Express 5 default) unless you need nesting
Dependencies of Note
- path-to-regexp (via router) -- prior ReDoS, parse() bugs (see path-to-regexp.md)
- qs --
allowPrototypes: truepassed by Express in extended mode - send -- symlink following by default
- proxy-addr -- IP trust evaluation
Open Questions (Resolved)
- ~~Has any modern public full-source review of Express core been published?~~ Resolved: This audit is the first documented proactive source review.
- ~~Which historical advisories belong on the package page versus the wider middleware ecosystem?~~ Partially resolved: Core findings documented above; middleware ecosystem audit remains open.
- ~~What routing and request-parsing assumptions should be tracked alongside path-to-regexp coverage?~~ Resolved: Query parsing (qs), hostname parsing, protocol parsing all audited.
Related Pages
- [[npm/path-to-regexp]]
- [[npm/koa-router]]
- [[npm/index]]
Last updated: 2026-04-10 | Sources: 8 (upstream repository, Express security policy, Express threat model, source code audit of Express 5.2.1 + 8 dependencies, OSV database, npm registry) Auditor contact: @travis-burmaster