Express.js: как получить адрес удаленного клиента

256

Я не совсем понимаю, как я должен получить IP-адрес удаленного пользователя.

Допустим, у меня есть простой маршрут запроса, такой как:

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

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

Правильный ли приведенный выше подход для получения реального IP-адреса пользователя или есть лучший способ? А как насчет прокси?

Erik
источник
1
Как насчет использования node-ipware согласно объяснению здесь .
un33k
если вы не можете получить req.hostname, например, «example.com»: stackoverflow.com/a/37824880/5333284
zhi.yang
1
Возможный дубликат Как определить IP-адрес пользователя в узле
Дан Даскалеску

Ответы:

469

Если вы работаете за прокси-сервером, таким как NGiNX или что-то еще, только тогда вы должны проверить «x-forwarded-for»:

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

Если прокси не «ваш», я бы не стал доверять заголовку «x-forwarded-for», потому что он может быть подделан.

alessioalex
источник
2
Это правильно, однако в моей ситуации мне пришлось использовать квадратные скобки (см. Выше). Также убедитесь, что в вашей конфигурации nginx включена опция x-forwarded-for. Работает как шарм!
девятьсот
43
Вы должны иметь в виду, что вы должны поместить эту директиву proxy_set_header X-Forwarded-For $remote_addr;в вашу конфигурацию nginx, если вы используете свой собственный обратный прокси.
Коксер
Если вы планируете использовать IP в браузере, тогда http (s): // [ipv6]: port Обратите внимание, что [] необходимы, надеюсь, это поможет
psuhas
43
Пользователи copy-pasta, пожалуйста, обратите внимание: это может вернуть список IP-адресов через запятую. У нас была ошибка от разработчика, копировавшего это и сравнившего результат с IP. Возможно, сделать что-то вроде, var ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress || '').split(',')[0].trim();чтобы получить клиентский IP.
Дэви Джонс
3
@Rishav :: 1 - это IPv6-адрес для localhost
Даниэль О,
238

Хотя ответ @alessioalex работает, существует другой способ, указанный в разделе « Экспресс за прокси » руководства Express .

  1. Добавьте app.set('trust proxy', true)к вашему коду экспресс-инициализации.
  2. Если вы хотите получить ip удаленного клиента, используйте req.ipили req.ipsобычным способом (как если бы не было обратного прокси)

Дополнительное чтение:

  • Используйте req.ipили req.ips. req.connection.remoteAddressне работает с этим решением.
  • 'trust proxy'Доступно больше опций для , если вам нужно что-то более сложное, чем доверять всему, что пропущено в x-forwarded-forзаголовке (например, когда ваш прокси не удаляет существующий заголовок x-forwarded-for из ненадежных источников). Смотрите связанное руководство для более подробной информации.
  • Если ваш прокси-сервер не заполняет x-forwarded-forзаголовок, есть две возможности.
    1. Прокси-сервер не передает информацию о том, где изначально был запрос. В этом случае не было бы способа выяснить, откуда исходил запрос. Вам нужно сначала изменить конфигурацию прокси-сервера.
      • Например, если вы используете nginx в качестве обратного прокси-сервера, вам может потребоваться добавить его proxy_set_header X-Forwarded-For $remote_addr;в свою конфигурацию.
    2. Прокси-сервер ретранслирует информацию о том, откуда исходил запрос, в проприетарной форме (например, настраиваемый заголовок http). В таком случае этот ответ не будет работать. Может быть специальный способ получить эту информацию, но вам нужно сначала понять механизм.
Haozhun
источник
4
Мой ответ был более общим (не привязанным к Express), но если вы используете Express, это действительно лучший способ.
Alessioalex
4
Ваш прокси-сервер должен иметь заголовок «x-forwarded-for», установленный на удаленный адрес. Например, в случае с nginx у вас должен быть proxy_set_header X-Forwarded-For $ remote_addr в вашем конфигурационном файле
Kamagatos
1
За IIS с IISnode в качестве прокси, app.enable('trust proxy')тоже работает, чтобы использовать req.ip. За исключением того, что у меня есть порт с ним 1.2.3.4:56789. Чтобы избавиться от этого, я делаюvar ip = req.ip.split(':')[0]
Кристиан Вестербик
Это решение безопасно?
Даниэль Кмак
3
Я чувствую, что этот ответ лишен безопасности и нуждается в обновлении. Вы должны ВСЕГДА определять, какие прокси доверяет вашему приложению. Принятый ответ, по крайней мере, имеет небольшое уведомление о подмене. Тем не менее, это лучшее решение использовать библиотеку, подобную этой, если вы используете Express, но цитируемый код неверен и не найден в связанном ресурсе.
Фил
54

В nginx.confфайле:
proxy_set_header X-Real-IP $remote_addr;

В node.jsфайле сервера:
var ip = req.headers['x-real-ip'] || req.connection.remoteAddress;

обратите внимание, что выражают заголовки в нижнем регистре

ququzone
источник
3
Добро пожаловать в стек переполнения! Вместо того, чтобы только публиковать блок кода, пожалуйста, объясните, почему этот код решает поставленную проблему. Без объяснения это не ответ.
Артемикс
1
Ответ @ququzone в порядке. В объяснении установлен специальный заголовок запроса с именем «x-real-ip», который получает исходный IP-адрес от посетителя. У меня это работает с node и socket.io.
coffekid
8
Для меня IP-адрес доступен req.headers['x-real-ip']даже в nginx.confзаголовке, он указан заглавными буквами.
Ник Сумейко
Это то, что решило это для меня. Даже с доверенным прокси-сервером, установленным в true, он все еще использовал локальный адрес 127.0.0.1
David
30

В частности, для узла документация для компонента http-сервера в разделе подключения к событиям гласит:

[Срабатывает] при создании нового потока TCP. Сокет [] является объектом типа net.Socket. Обычно пользователи не хотят получать доступ к этому событию. В частности, сокет не будет генерировать читаемые события из-за того, как синтаксический анализатор протокола присоединяется к сокету. Сокет также может быть доступен по адресу request.connection.

Таким образом, это означает, что request.connectionэто сокет, и, согласно документации, действительно есть атрибут socket.remoteAddress, который согласно документации:

Строковое представление удаленного IP-адреса. Например, '74 .125.127.100 'или' 2001: 4860: a005 :: 68 '.

В экспрессе объект запроса также является экземпляром объекта запроса http узла, поэтому этот подход все еще должен работать.

Однако в Express.js запрос уже имеет два атрибута: req.ip и req.ips

req.ip

Возврат удаленного адреса или, если «доверенный прокси» включен - адрес обратного потока.

req.ips

Когда «доверенный прокси» имеет значение true, проанализируйте список IP-адресов «X-Forwarded-For» и верните массив, в противном случае возвращается пустой массив. Например, если бы значением было «client, proxy1, proxy2», вы бы получили массив [«client», «proxy1», «proxy2»], где «proxy2» - самый дальний нисходящий поток.

Возможно, стоит упомянуть, что, по моему мнению, Express req.ipявляется более подходящим подходом, чем req.connection.remoteAddress, поскольку он req.ipсодержит фактический ip клиента (при условии, что доверенный прокси-сервер включен в Express), тогда как другой может содержать IP-адрес прокси-сервера (если есть). один).

Вот почему в настоящее время принятый ответ предполагает:

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

req.headers['x-forwarded-for']Будет эквивалентна экспресс req.ip.

Эдвин Далорсо
источник
11
  1. Добавить app.set('trust proxy', true)
  2. Используйте req.ipили req.ipsобычным способом
плешивый
источник
2
Это путь. См. Expressjs.com/en/guide/behind-proxies.html для получения подробной информации.
О. Джонс
Так просто и лаконично. Люблю простой ответ.
Гилберт-в
7

Это просто дополнительная информация для этого ответа .

Если вы используете nginx, вы бы добавить proxy_set_header X-Real-IP $remote_addr;в блок местоположения для сайта. /etc/nginx/sites-available/www.example.comнапример. Вот пример блока сервера.

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;
    }
}

После перезапуска nginxвы сможете получить доступ к ip в маршрутах вашего node/ expressприложения сreq.headers['x-real-ip'] || req.connection.remoteAddress;

learnsomemore
источник
3

По словам Express за прокси , req.ipучел обратный прокси, если вы настроили trust proxyправильно. Поэтому это лучше, чем то, req.connection.remoteAddressчто получается с сетевого уровня и не знает прокси.

сокр
источник
3

Я написал пакет для этой цели. Вы можете использовать его в качестве промежуточного программного обеспечения. Мой пакет опубликован здесь: https://www.npmjs.com/package/express-ip

Вы можете установить модуль используя

npm i express-ip

использование

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);
});
Ойтоке Тоби
источник
1
Вы должны раскрывать принадлежность в сообщении, когда ссылаетесь на то, с чем связаны. Если вы не раскрываете принадлежность, это считается спамом. Раскрытие должно быть явным, но не должно быть формальным (например, для вашего личного контента: «на моем сайте…», «в моем блоге…» и т. Д.). Видите: Что означает «Хорошее» самореклама? , Некоторые советы и рекомендации о саморекламе , Что такое точное определение понятия «спам» для переполнения стека? и что делает что-то спамом .
Макьен
2

Я знаю, что на этот вопрос ответили, но вот как я заставил свою работу работать.

let ip = req.connection.remoteAddress.split(`:`).pop();
Лорем Ипсум
источник
2

Если у вас все в порядке с использованием сторонней библиотеки. Можете проверить request-ip .

Вы можете использовать это по

import requestIp from 'request-ip';

app.use(requestIp.mw())

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

Исходный код довольно длинный, поэтому я не буду копировать здесь, вы можете проверить на https://github.com/pbojinov/request-ip/blob/master/src/index.js

В принципе,

Он ищет конкретные заголовки в запросе и возвращается к некоторым значениям по умолчанию, если они не существуют.

Пользователь ip определяется в следующем порядке:

  1. X-Client-IP
  2. X-Forwarded-For (Заголовок может возвращать несколько IP-адресов в формате: «IP-адрес клиента, IP-адрес прокси-сервера 1, IP-адрес прокси-сервера 2», поэтому мы выбираем первый.)
  3. CF-Connecting-IP (Cloudflare)
  4. Fastly-Client-Ip (Быстро CDN и Firebase хостинг заголовок при передаче в облачную функцию)
  5. True-Client-Ip (Akamai и Cloudflare)
  6. X-Real-IP (Nginx прокси / FastCGI)
  7. X-Cluster-Client-IP (Rackspace LB, Русло Стингрей)
  8. X-Forwarded, Forwarded-ForИ Forwarded(Вариации # 2)
  9. req.connection.remoteAddress
  10. req.socket.remoteAddress
  11. req.connection.socket.remoteAddress
  12. req.info.remoteAddress

Если IP-адрес не может быть найден, он вернется null.

Раскрыть: я не связан с библиотекой.

Хунбо Мяо
источник
1

Это сработало для меня лучше, чем остальные. Мои сайты отстают от CloudFlare и, похоже, требуют cf-connecting-ip.

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

Не проверял Express за прокси, так как он ничего не говорил об этом cf-connecting-ipзаголовке.

Ile
источник
1

С поддержкой-flare, nginx и 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
источник
1

В моем случае, аналогично этому решению, я использовал следующий подход x-forwarded-for :

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

x-forwarded-forВ заголовке будет продолжаться добавление маршрута IP от источника до конечного целевого сервера, поэтому, если вам нужно получить IP клиента источника, это будет первый элемент массива.

Менелаос Коцолларис
источник
0

var ip = req.connection.remoteAddress;

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

Булават Аджай
источник
результат равен: - :: ffff: XXX.XX.XX.XX, из этого мы получим ip
Bhulawat Ajay
3
Я думаю, что ip = ip.split(':').pop();будет бэттер в этом случае, если придет нормальный ip, т.е. 127.0.0.1. Он все еще сможет дать вам ip.
9
0

У объекта заголовков есть все, что вам нужно, просто сделайте это:

var ip = req.headers['x-forwarded-for'].split(',')[0];
Хуан Дэвид Арсе
источник
0

Собираем все вместе с помощью решения @kakopappa и morganрегистрируем IP-адрес клиента:

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
источник