11import type { Request } from 'express' ;
2- import requestIp from 'request-ip' ;
32
43/**
54 * Lit une en-tête HTTP (string ou première entrée d'un tableau).
@@ -37,73 +36,11 @@ function normalizeIp(raw: string | undefined | null): string | null {
3736 return value . startsWith ( '::ffff:' ) ? value . slice ( 7 ) : value ;
3837}
3938
40- function isLoopbackIp ( ip : string | null ) : boolean {
41- return ip === '127.0.0.1' || ip === '::1' ;
42- }
43-
44- function isPrivateIpv4 ( ip : string ) : boolean {
45- const parts = ip . split ( '.' ) . map ( ( p ) : number => Number . parseInt ( p , 10 ) ) ;
46- if ( parts . length !== 4 || parts . some ( ( n ) => Number . isNaN ( n ) || n < 0 || n > 255 ) ) {
47- return false ;
48- }
49- const [ a , b ] = parts ;
50- if ( a === 10 ) {
51- return true ;
52- }
53- if ( a === 172 && b >= 16 && b <= 31 ) {
54- return true ;
55- }
56- if ( a === 192 && b === 168 ) {
57- return true ;
58- }
59- return false ;
60- }
61-
62- function hasForwardingHeaders ( req : Request ) : boolean {
63- const forwarded = [ 'x-forwarded-for' , 'x-real-ip' , 'cf-connecting-ip' , 'true-client-ip' ] as const ;
64- return forwarded . some ( ( name ) => Boolean ( normalizeIp ( firstCsvSegment ( headerString ( req , name ) ) ?? headerString ( req , name ) ?? null ) ) ) ;
65- }
66-
67- function hostLooksLocal ( req : Request ) : boolean {
68- const host = headerString ( req , 'host' ) ;
69- if ( ! host ) {
70- return false ;
71- }
72- const hostname = host . split ( ':' ) [ 0 ] ?. trim ( ) . toLowerCase ( ) ;
73- return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' ;
74- }
75-
76- function localDevFallbackIp ( req : Request , peerIp : string | null ) : string | null {
77- if ( ! hostLooksLocal ( req ) || hasForwardingHeaders ( req ) || ! peerIp ) {
78- return null ;
79- }
80- if ( isLoopbackIp ( peerIp ) || isPrivateIpv4 ( peerIp ) ) {
81- return null ;
82- }
83- return '127.0.0.1' ;
84- }
85-
8639/** Paire TCP (souvent le dernier proxy / relai), normalisée. */
8740function tcpPeerIp ( req : Request ) : string | null {
8841 return normalizeIp ( req . socket ?. remoteAddress ?? null ) ;
8942}
9043
91- /**
92- * Nitro / certains proxies posent X-Forwarded-For = IP du pair TCP sans ajouter la vraie IP client.
93- * Dans ce cas l’en-tête est trompeur : on l’ignore pour request-ip aussi.
94- */
95- function requestForIpResolution ( req : Request ) : Request {
96- const peer = tcpPeerIp ( req ) ;
97- const xffRaw = headerString ( req , 'x-forwarded-for' ) ;
98- const xffFirst = normalizeIp ( firstCsvSegment ( xffRaw ) ?? ( xffRaw ?. trim ( ) || null ) ) ;
99- if ( ! peer || ! xffFirst || xffFirst !== peer ) {
100- return req ;
101- }
102- const headers = { ...req . headers } as Request [ 'headers' ] ;
103- delete headers [ 'x-forwarded-for' ] ;
104- return { ...req , headers } as Request ;
105- }
106-
10744/**
10845 * Résout l'IP client réelle derrière CDN / reverse-proxy (Cloudflare, nginx, etc.).
10946 * À utiliser pour l'auth et les audits ; combiner avec `trust proxy` sur Express si besoin.
@@ -114,10 +51,6 @@ function requestForIpResolution(req: Request): Request {
11451 */
11552export function resolveClientIp ( req : Request ) : string | null {
11653 const peerIp = tcpPeerIp ( req ) ;
117- const forcedLocalIp = localDevFallbackIp ( req , peerIp ) ;
118- if ( forcedLocalIp ) {
119- return forcedLocalIp ;
120- }
12154 const orderedHeaders = [ 'cf-connecting-ip' , 'true-client-ip' , 'x-real-ip' , 'x-forwarded-for' ] as const ;
12255
12356 for ( const name of orderedHeaders ) {
@@ -134,11 +67,6 @@ export function resolveClientIp(req: Request): string | null {
13467 return ip ;
13568 }
13669
137- const fromLib = normalizeIp ( requestIp . getClientIp ( requestForIpResolution ( req ) ) ?? null ) ;
138- if ( fromLib ) {
139- return fromLib ;
140- }
141-
14270 const expressIp = normalizeIp ( req . ip ?? null ) ;
14371 if ( expressIp ) {
14472 return expressIp ;
0 commit comments