Skip to content

Commit

Permalink
fix(remix): Attempt to extract user IP from request headers. (#6263)
Browse files Browse the repository at this point in the history
Remix requests don't contain client IP addresses in `req.ip` or `req.socket.*`.
To extract them, we need to iterate over a set of request headers that may contain that info.

I have vendored / modified an implementation of that function from third-party utility set [`remix-utils`](https://github.com/sergiodxa/remix-utils#getclientipaddress). (Using as a dependency is not possible because of incompatible TS syntax.)

Co-authored-by: Katie Byers <katie.byers@sentry.io>
  • Loading branch information
onurtemizkan and lobsterkatie committed Nov 24, 2022
1 parent 94f4ae9 commit b52336b
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
85 changes: 85 additions & 0 deletions packages/remix/src/utils/getIpAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Vendored / modified from @sergiodxa/remix-utils
// https://github.com/sergiodxa/remix-utils/blob/02af80e12829a53696bfa8f3c2363975cf59f55e/src/server/get-client-ip-address.ts

import { isIP } from 'net';

/**
* Get the IP address of the client sending a request.
*
* It receives a Request headers object and use it to get the
* IP address from one of the following headers in order.
*
* - X-Client-IP
* - X-Forwarded-For
* - Fly-Client-IP
* - CF-Connecting-IP
* - Fastly-Client-Ip
* - True-Client-Ip
* - X-Real-IP
* - X-Cluster-Client-IP
* - X-Forwarded
* - Forwarded-For
* - Forwarded
*
* If the IP address is valid, it will be returned. Otherwise, null will be
* returned.
*
* If the header values contains more than one IP address, the first valid one
* will be returned.
*/
export function getClientIPAddress(headers: Headers): string | null {
// The headers to check, in priority order
const headerNames = [
'X-Client-IP',
'X-Forwarded-For',
'Fly-Client-IP',
'CF-Connecting-IP',
'Fastly-Client-Ip',
'True-Client-Ip',
'X-Real-IP',
'X-Cluster-Client-IP',
'X-Forwarded',
'Forwarded-For',
'Forwarded',
];

// This will end up being Array<string | string[] | undefined | null> because of the various possible values a header
// can take
const headerValues = headerNames.map((headerName: string) => {
const value = headers.get(headerName);

if (headerName === 'Forwarded') {
return parseForwardedHeader(value);
}

return value?.split(', ');
});

// Flatten the array and filter out any falsy entries
const flattenedHeaderValues = headerValues.reduce((acc: string[], val) => {
if (!val) {
return acc;
}

return acc.concat(val);
}, []);

// Find the first value which is a valid IP address, if any
const ipAddress = flattenedHeaderValues.find(ip => ip !== null && isIP(ip));

return ipAddress || null;
}

function parseForwardedHeader(value: string | null): string | null {
if (!value) {
return null;
}

for (const part of value.split(';')) {
if (part.startsWith('for=')) {
return part.slice(4);
}
}

return null;
}
13 changes: 13 additions & 0 deletions packages/remix/src/utils/web-fetch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Based on Remix's implementation of Fetch API
// https://github.com/remix-run/web-std-io/tree/main/packages/fetch

import { getClientIPAddress } from './getIpAddress';
import { RemixRequest } from './types';

/*
Expand Down Expand Up @@ -92,6 +93,15 @@ export const normalizeRemixRequest = (request: RemixRequest): Record<string, any
headers.set('Connection', 'close');
}

let ip;

// Using a try block here just to stay on the safe side
try {
ip = getClientIPAddress(headers);
} catch (e) {
// ignore
}

// HTTP-network fetch step 4.2
// chunked encoding is handled by Node.js
const search = getSearch(parsedURL);
Expand All @@ -116,6 +126,9 @@ export const normalizeRemixRequest = (request: RemixRequest): Record<string, any

// [SENTRY] For compatibility with Sentry SDK RequestData parser, adding `originalUrl` property.
originalUrl: parsedURL.href,

// [SENTRY] Adding `ip` property if found inside headers.
ip,
};

return requestOptions;
Expand Down

0 comments on commit b52336b

Please sign in to comment.