Express.js: jak uzyskać zdalny adres klienta

256

Nie do końca rozumiem, jak powinienem uzyskać adres IP użytkownika zdalnego.

Powiedzmy, że mam prostą trasę żądania, taką jak:

app.get(/, function (req, res){
   var forwardedIpsStr = req.header('x-forwarded-for');
   var IP = '';

   if (forwardedIpsStr) {
      IP = forwardedIps = forwardedIpsStr.split(',')[0];  
   }
});

Czy powyższe podejście jest prawidłowe, aby uzyskać rzeczywisty adres IP użytkownika, czy istnieje lepszy sposób? A co z proxy?

Erik
źródło
1
Co powiesz na użycie węzła ipware zgodnie z wyjaśnieniem tutaj .
un33k
jeśli nie możesz uzyskać wymaganej nazwy hosta, takiej jak „example.com”: stackoverflow.com/a/37824880/5333284
zhi.yang
1
Możliwy duplikat sposobu określania adresu IP użytkownika w węźle
Dan Dascalescu

Odpowiedzi:

469

Jeśli korzystasz z proxy, takiego jak NGiNX lub co masz, tylko wtedy powinieneś sprawdzić „x-forwarded-for”:

var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

Jeśli proxy nie jest „twoje”, nie ufałbym nagłówkowi „x-forwarded-for”, ponieważ można go sfałszować.

alessioalex
źródło
2
Jest to poprawne, jednak w mojej sytuacji musiałem użyć nawiasów kwadratowych (patrz wyżej edycja). Upewnij się także, że masz włączone przekazywanie x w konfiguracji nginx. Działa jak marzenie!
setek
43
Należy pamiętać, że należy wprowadzić tę dyrektywę proxy_set_header X-Forwarded-For $remote_addr;do konfiguracji nginx na wypadek korzystania z własnego odwrotnego proxy.
Coxer
Jeśli planujesz używać adresu IP w przeglądarce, http (s): // [ipv6]: port Zauważ, że [] są potrzebne, mam nadzieję, że to pomoże
psuhas
43
Uwaga: użytkownicy kopiujący makaron powinni zwrócić listę adresów IP oddzieloną przecinkami. Mieliśmy błąd od dewelopera kopiującego to i porównującego wynik z adresem IP. Być może zrób coś takiego, var ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress || '').split(',')[0].trim();aby uzyskać adres IP klienta.
Davy Jones
3
@Rishav :: 1 to adres IPv6 dla hosta lokalnego
Daniel O
238

Podczas gdy odpowiedź z @alessioalex działa, istnieje inny sposób, jak podano w Express za sekcją proxy w Express - przewodnik .

  1. Dodaj app.set('trust proxy', true) do ekspresowego kodu inicjalizacji.
  2. Jeśli chcesz uzyskać adres IP zdalnego klienta, użyj req.iplub req.ipsw zwykły sposób (tak jakby nie było odwrotnego proxy)

Opcjonalne czytanie:

  • Użyj req.iplub req.ips.req.connection.remoteAddressnie działa z tym rozwiązaniem.
  • Więcej opcji 'trust proxy'dostępnych jest, jeśli potrzebujesz czegoś bardziej wyrafinowanego niż ufanie, że wszystko przeszłox-forwarded-for nagłówku (na przykład, gdy twój serwer proxy nie usuwa wcześniej istniejącego nagłówka przekierowanego x z niezaufanych źródeł). Więcej informacji można znaleźć w połączonym przewodniku.
  • Jeśli twój serwer proxy nie zapełnił x-forwarded-fornagłówka, istnieją dwie możliwości.
    1. Serwer proxy nie przekazuje informacji o pierwotnym żądaniu. W takim przypadku nie byłoby sposobu, aby dowiedzieć się, skąd pochodziło żądanie. Najpierw musisz zmodyfikować konfigurację serwera proxy.
      • Na przykład, jeśli używasz nginx jako odwrotnego proxy, może być konieczne dodanie go proxy_set_header X-Forwarded-For $remote_addr;do konfiguracji.
    2. Serwer proxy przekazuje informacje o tym, skąd pierwotnie pochodziło żądanie, w zastrzeżony sposób (na przykład niestandardowy nagłówek http). W takim przypadku odpowiedź nie zadziała. Może istnieć niestandardowy sposób na uzyskanie tych informacji, ale najpierw musisz zrozumieć mechanizm.
Haozhun
źródło
4
Moja odpowiedź była bardziej ogólna (niezwiązana z Express), ale jeśli używasz Express, to rzeczywiście lepszy sposób.
alessioalex
4
Twój serwer proxy musi mieć ustawiony nagłówek „x-forwarded-for” na adres zdalny. Na przykład w przypadku nginx powinieneś mieć proxy_set_header X-Forwarded-For $ remote_addr w pliku konfiguracyjnym
Kamagatos
1
Za IIS z IISnode jako serwerem proxy app.enable('trust proxy')również działa, aby go użyć req.ip. Tyle że dostaję port 1.2.3.4:56789. Aby to rozebrać, robię tovar ip = req.ip.split(':')[0]
Christiaan Westerbeek,
Czy to rozwiązanie jest bezpieczne?
Daniel Kmak,
3
Uważam, że ta odpowiedź nie ma bezpieczeństwa i wymaga aktualizacji. ZAWSZE powinieneś określać, które serwery proxy ufają twojej aplikacji. Przyjęta odpowiedź ma przynajmniej trochę informacji o fałszowaniu. To powiedziawszy, lepszym rozwiązaniem jest użycie takiej biblioteki, jeśli używasz ekspresowego, ale cytowany kod jest niepoprawny i nie można go znaleźć w połączonym zasobie.
Phil
54

W nginx.confpliku:
proxy_set_header X-Real-IP $remote_addr;

W node.jspliku serwera:
var ip = req.headers['x-real-ip'] || req.connection.remoteAddress;

zwróć uwagę, że wyrażaj małe litery w nagłówkach

ququzone
źródło
3
Witamy w Stack Overflow! Zamiast pisać tylko blok kodu, wyjaśnij, dlaczego ten kod rozwiązuje postawiony problem. Bez wyjaśnienia nie jest to odpowiedź.
Artemix,
1
Odpowiedź @ququzone jest w porządku. Wyjaśnienie to ustawia niestandardowy nagłówek żądania o nazwie „x-real-ip”, który pobiera oryginalny adres IP od gościa. Działa dla mnie z node i socket.io.
coffekid
8
Dla mnie adres IP jest dostępny pod req.headers['x-real-ip']nawet w nginx.confnagłówku ustawionym wielkimi literami.
Nik Sumeiko,
To właśnie dla mnie to rozwiązało. Nawet przy ustawieniu true-proxy na wartość true nadal korzystał z lokalnego adresu 127.0.0.1
David
30

Dokumentacja komponentu serwera http, szczególnie dla węzła, w przypadku połączenia ze zdarzeniem mówi:

[Wyzwolony] po ustanowieniu nowego strumienia TCP. [Gniazdo] jest obiektem typu net.Socket. Zwykle użytkownicy nie będą chcieli uzyskać dostępu do tego wydarzenia. W szczególności gniazdo nie będzie emitować czytelnych zdarzeń ze względu na sposób, w jaki parser protokołu łączy się z gniazdem. Dostęp do gniazda można również uzyskać pod adresem request.connection.

Oznacza request.connectionto , że jest to gniazdo i zgodnie z dokumentacją rzeczywiście istnieje atrybut socket.remoteAddress, który zgodnie z dokumentacją to:

Ciąg reprezentujący zdalny adres IP. Na przykład „74 .125.127.100” lub „2001: 4860: a005 :: 68”.

W trybie express obiekt żądania jest również instancją obiektu żądania Node http, więc to podejście powinno nadal działać.

Jednak w Express.js żądanie ma już dwa atrybuty: req.ip i req.ips

req.ip

Zwróć adres zdalny lub gdy „zaufane proxy” jest włączone - adres nadrzędny.

req.ips

Gdy „proxy proxy” jest true, przeanalizuj listę adresów IP „X-Forwarded-For” i zwróć tablicę, w przeciwnym razie zwracana jest pusta tablica. Na przykład, jeśli wartością byłoby „klient, proxy1, proxy2”, otrzymalibyście tablicę [„klient”, „proxy1”, „proxy2”], gdzie „proxy2” jest najdalszym strumieniem zstępującym.

Warto wspomnieć, że moim zdaniem Express req.ipjest lepszym podejściem niż req.connection.remoteAddressod tamtej poryreq.ip zawiera rzeczywisty adres IP klienta (pod warunkiem, że zaufane proxy jest włączone w trybie ekspresowym), podczas gdy drugi może zawierać adres IP proxy (jeśli istnieje jeden).

Z tego powodu obecnie akceptowana odpowiedź sugeruje:

var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

req.headers['x-forwarded-for']Będzie równowartość wyraźnej req.ip.

Edwin Dalorzo
źródło
11
  1. Dodaj app.set('trust proxy', true)
  2. Użyj req.iplub req.ipsw zwykły sposób
Łysy
źródło
2
To jest odpowiednie rozwiązanie. Szczegółowe informacje można znaleźć na stronie expressjs.com/en/guide/behind-proxies.html .
O. Jones
Tak łatwe i zwięzłe. Uwielbiam prostą odpowiedź.
gilbert-v
7

To tylko dodatkowe informacje na temat tej odpowiedzi .

Jeśli używasz nginx, dodajesz proxy_set_header X-Real-IP $remote_addr;do bloku lokalizacji dla witryny. /etc/nginx/sites-available/www.example.comna przykład. Oto przykładowy blok serwera.

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    location / {
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_pass http://127.0.1.1:3080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Po ponownym uruchomieniu nginxbędziesz mógł uzyskać dostęp do adresu IP na trasach node/ expressaplikacji za pomocąreq.headers['x-real-ip'] || req.connection.remoteAddress;

Dowiedz się więcej
źródło
3

Według Express za serwerami proxy , req.ipwziął pod uwagę odwrotny serwer proxy, jeśli trust proxypoprawnie skonfigurowałeś . Dlatego jest lepszy niż ten, req.connection.remoteAddressktóry jest uzyskiwany z warstwy sieci i nieświadomy proxy.

abbr
źródło
3

W tym celu napisałem paczkę. Możesz użyć go jako ekspresowego oprogramowania pośredniego. Mój pakiet został opublikowany tutaj: https://www.npmjs.com/package/express-ip

Możesz zainstalować moduł za pomocą

npm i express-ip

Stosowanie

const express = require('express');
const app = express();
const expressip = require('express-ip');
app.use(expressip().getIpInfoMiddleware);

app.get('/', function (req, res) {
    console.log(req.ipInfo);
});
Oyetoke Tobi
źródło
1
Musisz ujawnić powiązanie w poście podczas linkowania do czegoś, z czym jesteś powiązany. Jeśli nie ujawnisz powiązania, zostanie to uznane za spam. Ujawnienie informacji musi być jawne, ale nie musi być formalne (np. Dla celów osobistych treści : „na mojej stronie…”, „na moim blogu…” itp.). Zobacz: Co oznacza „dobra” autopromocja? , Kilka wskazówek i porad na temat autopromocji , Jaka jest dokładna definicja „spam” na przepełnienie stosu? i Co powoduje, że coś jest spamem .
Makyen
2

Wiem, że odpowiedziano na to pytanie, ale oto jak dostałem moje do pracy.

let ip = req.connection.remoteAddress.split(`:`).pop();
Lorem Ipsum
źródło
2

Jeśli wszystko w porządku, korzystając z biblioteki innej firmy. Możesz sprawdzić request-ip .

Możesz go użyć jest przez

import requestIp from 'request-ip';

app.use(requestIp.mw())

app.use((req, res) => {
  const ip = req.clientIp;
});

Kod źródłowy jest dość długi, więc nie będę tutaj kopiować, możesz sprawdzić na https://github.com/pbojinov/request-ip/blob/master/src/index.js

Gruntownie,

Wyszukuje określone nagłówki w żądaniu i wraca do niektórych wartości domyślnych, jeśli nie istnieją.

Adres IP użytkownika jest określany według następującej kolejności:

  1. X-Client-IP
  2. X-Forwarded-For (Nagłówek może zwrócić wiele adresów IP w formacie: „adres IP klienta, adres IP serwera proxy 1, adres IP serwera proxy 2”, więc przyjmujemy pierwszy).
  3. CF-Connecting-IP (Cloudflare)
  4. Fastly-Client-Ip (Fastly CDN i nagłówek hosta Firebase, gdy są wysyłane do funkcji chmury)
  5. True-Client-Ip (Akamai i Cloudflare)
  6. X-Real-IP (Nginx proxy / FastCGI)
  7. X-Cluster-Client-IP (Rackspace LB, Riverbed Stingray)
  8. X-Forwarded, Forwarded-ForI Forwarded(odmiany # 2)
  9. req.connection.remoteAddress
  10. req.socket.remoteAddress
  11. req.connection.socket.remoteAddress
  12. req.info.remoteAddress

Jeśli nie można znaleźć adresu IP, zostanie on zwrócony null.

Ujawnij: Nie jestem powiązany z biblioteką.

Hongbo Miao
źródło
1

To działało dla mnie lepiej niż reszta. Moje strony stoją za CloudFlare i wydaje się, że wymaga cf-connecting-ip.

req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress

Nie testowałem Express za serwerami proxy, ponieważ nic nie mówiło o tym cf-connecting-ipnagłówku.

ile
źródło
1

Z obsługą Flare, Nginx i X-Real-IP

var user_ip;

    if(req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
      let first = req.headers['cf-connecting-ip'].split(', ');
      user_ip = first[0];
    } else {
      let user_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
    }
kakopappa
źródło
1

W moim przypadku, podobnie jak w przypadku tego rozwiązania, ostatecznie zastosowałem następujące podejście X-forwarded-for :

let ip = (req.headers['x-forwarded-for'] || '').split(',')[0];

x-forwarded-fornagłówek będzie kontynuował dodawanie trasy adresu IP od źródła aż do końcowego serwera docelowego, więc jeśli chcesz pobrać adres IP klienta pochodzenia, byłby to pierwszy element tablicy.

Menelaos Kotsollaris
źródło
0

var ip = req.connection.remoteAddress;

ip = ip.split (':') [3];

Bhulawat Ajay
źródło
ouput jest jak: - :: ffff: XXX.XX.XX.XX z tego otrzymamy ip
Bhulawat Ajay
3
Myślę, że ip = ip.split(':').pop();w tym przypadku będzie lepiej, jeśli przyjdzie normalne IP, tj. 127.0.0.1. Nadal będzie w stanie dać ci ip.
9me
0

Obiekt nagłówków ma wszystko, czego potrzebujesz, po prostu zrób to:

var ip = req.headers['x-forwarded-for'].split(',')[0];
Juan David Arce
źródło
0

Łączenie wszystkich rozwiązań witk @kakopappa plus morganlogowanie adresu IP klienta:

morgan.token('client_ip', function getId(req) {
    return req.client_ip
});
const LOG_OUT = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" :client_ip'
self.app.use(morgan(LOG_OUT, {
    skip: function(req, res) { // custom logging: filter status codes
        return res.statusCode < self._options.logging.statusCode;
    }
}));

// could-flare, nginx and x-real-ip support
var getIpInfoMiddleware = function(req, res, next) {
    var client_ip;
    if (req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
        var first = req.headers['cf-connecting-ip'].split(', ');
        client_ip = first[0];
    } else {
        client_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
    }
    req.client_ip = client_ip;
    next();
};
self.app.use(getIpInfoMiddleware);
loretoparisi
źródło