Overview
Undici is a high-performance HTTP/1.1 client written for Node.js that can be used in place of the native library. Part of the Fetch Standard indicates that forbidden request headers, such as cookies, should never be passed to the target site upon a redirect. Versions of undici prior to 5.26.2 failed to do that. (Note that for Node 16 this is a concern only when the --experimental-fetch flag is used.)
This vulnerability may be combined with the Open Redirect exploit in which an attacker gets a server to redirect to a site they control. If sensitive data, such as authorization tokens, is contained in the cookie, the destination site (which may be controlled by the attacker), receives the sensitive data.
This issue affects undici versions lower than 5.26.2.
Details
Module Info
- Product: Node.js
- Affected packages: Node.js (via undici)
- Affected versions: >=16.0.0 <=16.20.2
- GitHub repository: https://github.com/nodejs/node, https://github.com/nodejs/undici
- Published packages: https://undici.nodejs.org/
- Package manager: -
Vulnerability Info
This Low-severity vulnerability is found in undici in versions lower than 5.26.2.
undici can be imported independently and is also included as part of the Node.js distribution. Generally, it is best to upgrade Node.js to a version that includes the fixed version of the library rather than trying to upgrade just the undici library.
Steps To Reproduce
- Import a vulnerable version of undici or set up an instance of a version of Node.js that contains a vulnerable version of undici.
- Use the code below, which was taken from the test for this vulnerability that is included with the fix:
'use strict'
const { test } = require('tap')
const { createServer } = require('http')
const { once } = require('events')
const { fetch } = require('../..')
test('Cross-origin redirects clear forbidden headers', async (t) => {
t.plan(5)
const server1 = createServer((req, res) => {
t.equal(req.headers.cookie, undefined)
t.equal(req.headers.authorization, undefined)
res.end('redirected')
}).listen(0)
const server2 = createServer((req, res) => {
t.equal(req.headers.authorization, 'test')
t.equal(req.headers.cookie, 'ddd=dddd')
res.writeHead(302, {
...req.headers,
Location: `http://localhost:${server1.address().port}`
})
res.end()
}).listen(0)
t.teardown(() => {
server1.close()
server2.close()
})
await Promise.all([
once(server1, 'listening'),
once(server2, 'listening')
])
const res = await fetch(`http://localhost:${server2.address().port}`, {
headers: {
Authorization: 'test',
Cookie: 'ddd=dddd'
}
})
const text = await res.text()
t.equal(text, 'redirected')
})
- Note that the lines below assert that the specific headers are undefined:
t.equal(req.headers.cookie, undefined)
t.equal(req.headers.authorization, undefined)
- On a vulnerable system, these assertions will fail because Server1 will receive the Authorization and Cookie headers.
Mitigation
undici is a popular library that was added in Node 16 (in addition to the native HTTP library). All users that use the experimental fetch() should upgrade immediately.
Users of Node.js versions that are no longer supported should apply one of te following mitigations:
- Upgrade affected applications to supported versions of Node.js.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Additional Resources
- NIST entry: https://nvd.nist.gov/vuln/detail/CVE-2023-45143
- GitHub advisory: https://github.com/advisories/GHSA-wqq4-5wpv-mx2g