11import rateLimit , { ipKeyGenerator } from 'express-rate-limit' ;
22import fs from 'fs' ;
3+ import net from 'node:net' ;
34import { authCache } from './server.js' ;
45
56const maxLimitReq = parseInt ( process . env . RATE_LIMIT ) || 50 ;
67const bannedIpsFile = './banned-ips.log' ;
8+ const trustedProxyCidrsRaw = String ( process . env . TRUSTED_PROXY_CIDRS || '' ) . trim ( ) ;
9+ const trustedProxyAllowlist = new net . BlockList ( ) ;
10+ let trustedProxyRulesLoaded = false ;
711
812function sanitizeIpValue ( value ) {
913 if ( typeof value !== 'string' ) return '' ;
10- const cleaned = value . replace ( / [ \r \n \t ] / g, '' ) . trim ( ) ;
14+ let cleaned = value . replace ( / [ \r \n \t ] / g, '' ) . trim ( ) ;
1115 if ( ! cleaned ) return '' ;
12- return / ^ [ A - F a - f 0 - 9 : . ] + $ / . test ( cleaned ) ? cleaned : '' ;
16+ if ( cleaned . startsWith ( '::ffff:' ) ) {
17+ cleaned = cleaned . slice ( 7 ) ;
18+ }
19+ if ( cleaned . startsWith ( '[' ) && cleaned . endsWith ( ']' ) ) {
20+ cleaned = cleaned . slice ( 1 , - 1 ) ;
21+ }
22+ if ( ! cleaned ) return '' ;
23+ return net . isIP ( cleaned ) ? cleaned : '' ;
24+ }
25+
26+ function loadTrustedProxyRules ( ) {
27+ if ( trustedProxyRulesLoaded ) return ;
28+ trustedProxyRulesLoaded = true ;
29+ if ( ! trustedProxyCidrsRaw ) return ;
30+
31+ const rules = trustedProxyCidrsRaw
32+ . split ( / [ \s , ] + / )
33+ . map ( ( entry ) => entry . trim ( ) )
34+ . filter ( Boolean ) ;
35+
36+ for ( const rule of rules ) {
37+ const [ address , prefix ] = rule . split ( '/' ) ;
38+ const sanitizedAddress = sanitizeIpValue ( address ) ;
39+ const prefixNum = Number ( prefix ) ;
40+ const ipVersion = net . isIP ( sanitizedAddress ) ;
41+
42+ if ( ! sanitizedAddress || Number . isNaN ( prefixNum ) || ! Number . isInteger ( prefixNum ) || ! ipVersion ) {
43+ console . warn ( `Skipping invalid TRUSTED_PROXY_CIDRS entry: ${ rule } ` ) ;
44+ continue ;
45+ }
46+
47+ const type = ipVersion === 4 ? 'ipv4' : 'ipv6' ;
48+ trustedProxyAllowlist . addSubnet ( sanitizedAddress , prefixNum , type ) ;
49+ }
50+ }
51+
52+ function isTrustedProxyIp ( ip ) {
53+ loadTrustedProxyRules ( ) ;
54+ if ( ! trustedProxyCidrsRaw ) return false ;
55+ const sanitizedIp = sanitizeIpValue ( ip ) ;
56+ if ( ! sanitizedIp ) return false ;
57+ const type = net . isIP ( sanitizedIp ) === 4 ? 'ipv4' : 'ipv6' ;
58+ return trustedProxyAllowlist . check ( sanitizedIp , type ) ;
1359}
1460
1561export function addIpToBannedList ( ip ) {
@@ -32,19 +78,24 @@ export function addIpToBannedList(ip) {
3278}
3379
3480export function getClientIp ( req ) {
35- const cfConnectingIp = req . headers [ 'cf-connecting-ip' ] ;
36- if ( cfConnectingIp ) {
37- const ip = sanitizeIpValue ( cfConnectingIp . split ( ',' ) [ 0 ] ) ;
38- if ( ip ) return ip ;
39- }
81+ const peerIp = sanitizeIpValue ( req . socket ?. remoteAddress ) ;
82+ const trustForwardedHeaders = isTrustedProxyIp ( peerIp ) ;
83+
84+ if ( trustForwardedHeaders ) {
85+ const cfConnectingIp = req . headers [ 'cf-connecting-ip' ] ;
86+ if ( cfConnectingIp ) {
87+ const ip = sanitizeIpValue ( String ( cfConnectingIp ) . split ( ',' ) [ 0 ] ) ;
88+ if ( ip ) return ip ;
89+ }
4090
41- const forwardedFor = req . headers [ 'x-forwarded-for' ] ;
42- if ( forwardedFor ) {
43- const ip = sanitizeIpValue ( forwardedFor . split ( ',' ) [ 0 ] ) ;
44- if ( ip ) return ip ;
91+ const forwardedFor = req . headers [ 'x-forwarded-for' ] ;
92+ if ( forwardedFor ) {
93+ const ip = sanitizeIpValue ( String ( forwardedFor ) . split ( ',' ) [ 0 ] ) ;
94+ if ( ip ) return ip ;
95+ }
4596 }
4697
47- return sanitizeIpValue ( req . ip ) || req . ip ;
98+ return sanitizeIpValue ( req . ip ) || peerIp || 'unknown' ;
4899}
49100
50101function getRateLimitKey ( req ) {
0 commit comments