express icon indicating copy to clipboard operation
express copied to clipboard

Allow reading Forwarded header as alternative to X-Forwarded-

Open chriswilty opened this issue 1 year ago • 3 comments

A few service providers / proxy servers are beginning to use Forwarded header instead of X-Forwarded-For and related headers. This includes AWS's HTTP API Gateway.

Express does not currently support this header, and I can see no way of hooking up the trust proxy mechanism manually to parse this header, so I am a bit stuffed.

Can you either provide a config setting to use this header for trust proxy, or suggest an alternative way to get this to work?

chriswilty avatar Feb 08 '24 13:02 chriswilty

Just to answer the latter part, you can use any mechanism you need to get the proxy IP, you are not limited to x-forwarded-for only. Just define your own custom req.ip to do what you need https://expressjs.com/en/guide/overriding-express-api.html

dougwilson avatar Feb 08 '24 13:02 dougwilson

Thanks for the rapid response! That looks really promising, if I am allowed to overwrite the req.ip and req.secure flags. I couldn't find any reference to which Request properties are in which category (assigned or getters), but I'll see if those work for me....

chriswilty avatar Feb 08 '24 15:02 chriswilty

Works an absolute treat 🥳

secure-yessss

For anyone else coming here, I overrode request.protocol rather than request.secure. Code works for my use case - I can trust the left-most entry in Forwarded header, and can ignore X-Forwarded headers - but adapt as necessary.

const parseForwardedHeader = (request: typeof app.request) =>
	request.header('Forwarded')
		?.split(",")
		.flatMap((proxy) => proxy.split(';'))
		.reduce((result, proxyProps) => {
			const [key, value] = proxyProps.split('=');
			if (key && value) {
				result[key] = (result[key] || []).concat(value);
			}
			return result;
		}, {} as Record<string, string[]>);

Object.defineProperties(app.request, {
	'ip': {
		configurable: true,
		enumerable: true,
		get() {
			const proxies = parseForwardedHeader(this as typeof app.request);
			return proxies?.['for']?.[0] ?? this.socket.remoteAddress;
		},
	},
	'protocol': {
		configurable: true,
		enumerable: true,
		get() {
			const proxies = parseForwardedHeader(this as typeof app.request);
			return proxies?.['proto']?.[0] ?? this.socket.encrypted ? 'https' : 'http';
		},
	},
});

Importantly, for this use case don't enable trust proxy in case any in-between proxies are adding X-Forwarded- headers.

chriswilty avatar Feb 09 '24 11:02 chriswilty