From e0a3faaa39aa261f2c43c953957272f3cb1816c4 Mon Sep 17 00:00:00 2001 From: MrRobotBaguette Date: Sun, 5 Jul 2026 11:23:24 -0700 Subject: [PATCH] APICLIENT-3136: security: strip Cookie on cross-origin redirect and use full-origin check Co-Authored-By: Claude --- lib/redirect.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/redirect.js b/lib/redirect.js index 4d5fbb31f..5d4c492b2 100644 --- a/lib/redirect.js +++ b/lib/redirect.js @@ -123,14 +123,27 @@ Redirect.prototype.onResponse = function (response) { self.redirects.push({ statusCode: response.statusCode, redirectUri: redirectTo }) - // if the redirect hostname (not just port or protocol) is changed: + // if the redirect origin (protocol, hostname or port) is changed: // 1. remove host header, the new host will be populated on request.init - // 2. remove authorization header, avoid authentication leak + // 2. remove cookie header, avoid session leak across origins + // 3. remove authorization header, avoid authentication leak // @note: This is done because of security reasons, irrespective of the // status code or request method used. - if (request.headers && uriPrev.hostname !== request.uri.hostname) { + // @note: comparing full origin (protocol + hostname + port) so that + // credentials are not leaked across ports or on a protocol downgrade + // (e.g. https -> http), not just when the hostname changes. + var prevOrigin = uriPrev.protocol + '//' + uriPrev.hostname + (uriPrev.port ? ':' + uriPrev.port : '') + var nextOrigin = request.uri.protocol + '//' + request.uri.hostname + (request.uri.port ? ':' + request.uri.port : '') + if (request.headers && prevOrigin !== nextOrigin) { request.removeHeader('host') + // remove cookie header, avoid session cookie leak across origins + request.removeHeader('cookie') + + // clear the cached original cookie header so it isn't re-prepended + // to the Cookie header on subsequent hops (see Request.prototype.jar) + delete request.originalCookieHeader + // use followAuthorizationHeader option to retain authorization header if (!self.followAuthorizationHeader) { request.removeHeader('authorization')