Можно ли получить IP-адрес локальной сети пользователя с помощью JavaScript?

102

Я знаю, что первая реакция на этот вопрос - «нет», «это невозможно» и «вам это не нужно, вы делаете что-то не так». Я пытаюсь получить IP-адрес пользователя в локальной сети и отобразить его на веб-странице. Зачем? Потому что это то, над чем я работаю, чтобы показать как можно больше информации о вас, посетителе: http://www.whatsmyip.org/more-info-about-you/

Так что я на самом деле ничего не ДЕЛАЮ с IP, кроме как показывать его пользователю в информационных целях. Раньше я делал это с помощью небольшого Java-апплета. Это сработало очень хорошо. Но в наши дни браузер заставляет вас соглашаться и доверять столько раз, чтобы запустить даже самый незначительный Java-апплет, что я бы предпочел не запускать его вообще.

На какое-то время я просто избавился от этой функции, но, если возможно, хотел бы ее вернуть. Это было то, что я, как компьютерный консультант, время от времени действительно использовал. Быстрее перейти на этот веб-сайт, чтобы узнать, в каком диапазоне IP-адресов работает сеть, чем перейти в Системные настройки, Сеть и затем любой активный интерфейс.

Так что мне интересно, надеюсь, есть ли способ сделать это только на javascript? Может быть, у вас есть доступ к какому-то новому объекту, подобно тому, как javascript может запрашивать у браузера, где находится географическое положение на Земле. Может быть, есть что-то подобное для сетевой информации о клиентах? Если нет, может быть, есть какой-нибудь другой способ сделать это? Единственные способы, о которых я могу думать, - это java-апплет или flash-объект. Я бы предпочел не делать ничего из этого.

l008com
источник
1
Ты знаешь ответ. Зачем тогда спрашивать? Java-апплеты или флэш-объекты вряд ли будут разрешены пользователями (могут быть только теми, кто новичок в Интернете) - так что это не решение в общем случае. ActiveX и другие элементы работают только в IE - и, таким образом, пользователи других браузеров не пострадают (и, более того, даже в IE существует политика безопасности, которая не позволяет веб-сайту делать неприятные вещи)
Alma Do
Мой IP-адрес HTTP_X_FORWARDED_FORзаписан на этой странице, просто скажи.
tomdemuyt
51
Зачем тогда спрашивать? Потому что, может быть, просто возможно, я не все знаю.
l008com
1
Эти парни делают это: whatismyproxy.com
likebike
1
@likebike Хороший. Смотрим, как они это делают.
Доминик Черизано

Ответы:

117

Как оказалось, недавнее расширение HTML5 WebRTC позволяет javascript запрашивать IP-адрес локального клиента. Доказательство концепции доступно здесь: http://net.ipcalf.com

Эта функция, по- видимому по дизайну , и это не ошибка. Однако, учитывая его противоречивый характер, я бы с осторожностью полагался на такое поведение. Тем не менее, я думаю, что он идеально и надлежащим образом соответствует вашей намеченной цели (показывая пользователю, что происходит в его браузере).

в пути
источник
1
Это было полезно. Еще раз спасибо!
Ансурадж Хаданга 02
7
Он просто работает на Chrome и Firefox, И НЕ на IE, Edge или Safari
ali
Я искал свой IP-адрес в глобальной сети, и этот веб-сайт whatismyip.com также дал мне мой локальный IP-адрес, и я думаю, это как-то связано с JS.
Шаян
@ali Вы правы, упомянутый выше веб-сайт не может определить мой локальный IP-адрес на Edge.
Шаян
7
Google Chrome по умолчанию скрывает локальный IP-адрес. Он показывает что-то похожее на e87e041d-15e1-4662-adad-7a6601fca9fb.local . Это поведение можно изменить, установив для переменной # enable-webrtc-hide-local-ips-with-mdns значение disabled в Chrome: // flags
injaon
81

Обновить

Это решение больше не будет работать, потому что браузеры исправляют утечку webrtc: для получения дополнительной информации об этом прочтите другой вопрос: RTCIceCandidate больше не возвращает IP


В дополнение к ответу afourney этот код работает в браузерах, поддерживающих WebRTC (Chrome и Firefox). Я слышал, что есть движение по реализации функции, которая заставляет сайты запрашивать IP (например, в случае географического местоположения пользователя или пользовательского медиа), хотя это еще не реализовано ни в одном из этих браузеров.

Вот модифицированная версия исходного кода , уменьшенные строки, без запросов на оглушение, поскольку вам нужен только локальный IP, а не общедоступный IP:

window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;//compatibility for Firefox and chrome
var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};      
pc.createDataChannel('');//create a bogus data channel
pc.createOffer(pc.setLocalDescription.bind(pc), noop);// create offer and set local description
pc.onicecandidate = function(ice)
{
 if (ice && ice.candidate && ice.candidate.candidate)
 {
  var myIP = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1];
  console.log('my IP: ', myIP);   
  pc.onicecandidate = noop;
 }
};

Мы создаем фиктивное одноранговое соединение для удаленного однорангового узла, чтобы связаться с нами. Обычно мы обмениваемся ледяными кандидатами друг с другом и читая ледяных кандидатов, мы можем сообщить IP пользователя.

Вы можете найти демо в -> Демо

мидо
источник
Спасибо за это, Мидо! Очень признателен.
Суджай Пхадке
1
@dampee - я считаю, что Edge на данный момент не поддерживает каналы данных.
MichaelB76
Что такое фиктивный канал данных? Не могу найти никаких ссылок в Google
AmazingTurtle
2
обратите внимание, что api createOffer переключился на Promise вместо successCallback и failCallback в качестве параметров, поэтому это может не работать в более новых версиях, см .: developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/…
Dickeylth
10

WebRTC API может быть использован для получения локального IP клиента.

Однако браузер может не поддерживать его или клиент отключил его по соображениям безопасности. В любом случае, не следует полагаться на этот «хак» в долгосрочной перспективе, так как он, вероятно, будет исправлен в будущем (см. Ответ Каллена Пушистого Дженнингса).

Код ECMAScript 6 ниже демонстрирует, как это сделать.

/* ES6 */
const findLocalIp = (logInfo = true) => new Promise( (resolve, reject) => {
    window.RTCPeerConnection = window.RTCPeerConnection 
                            || window.mozRTCPeerConnection 
                            || window.webkitRTCPeerConnection;

    if ( typeof window.RTCPeerConnection == 'undefined' )
        return reject('WebRTC not supported by browser');

    let pc = new RTCPeerConnection();
    let ips = [];

    pc.createDataChannel("");
    pc.createOffer()
     .then(offer => pc.setLocalDescription(offer))
     .catch(err => reject(err));
    pc.onicecandidate = event => {
        if ( !event || !event.candidate ) {
            // All ICE candidates have been sent.
            if ( ips.length == 0 )
                return reject('WebRTC disabled or restricted by browser');

            return resolve(ips);
        }

        let parts = event.candidate.candidate.split(' ');
        let [base,componentId,protocol,priority,ip,port,,type,...attr] = parts;
        let component = ['rtp', 'rtpc'];

        if ( ! ips.some(e => e == ip) )
            ips.push(ip);

        if ( ! logInfo )
            return;

        console.log(" candidate: " + base.split(':')[1]);
        console.log(" component: " + component[componentId - 1]);
        console.log("  protocol: " + protocol);
        console.log("  priority: " + priority);
        console.log("        ip: " + ip);
        console.log("      port: " + port);
        console.log("      type: " + type);

        if ( attr.length ) {
            console.log("attributes: ");
            for(let i = 0; i < attr.length; i += 2)
                console.log("> " + attr[i] + ": " + attr[i+1]);
        }

        console.log();
    };
} );

Обратите внимание, я пишу return resolve(..)или return reject(..)как ярлык. Обе эти функции ничего не возвращают.

Тогда у вас может получиться что-то такое:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Local IP</title>
</head>
<body>
    <h1>My local IP is</h1>
    <p id="ip">Loading..</p>
    <script src="ip.js"></script>
    <script>
    let p = document.getElementById('ip');
    findLocalIp().then(
        ips => {
            let s = '';
            ips.forEach( ip => s += ip + '<br>' );
            p.innerHTML = s;
        },
        err => p.innerHTML = err
    );
    </script>
</body>
</html>
Linblow
источник
9

Я очистил сообщение mido, а затем убрал функцию, которую они нашли. Это либо вернет, falseлибо array. При тестировании помните, что вам нужно свернуть массив в консоли веб-разработчика, иначе его неинтуитивное поведение по умолчанию может ввести вас в заблуждение и вы подумаете, что он возвращает пустое значение array.

function ip_local()
{
 var ip = false;
 window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || false;

 if (window.RTCPeerConnection)
 {
  ip = [];
  var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};
  pc.createDataChannel('');
  pc.createOffer(pc.setLocalDescription.bind(pc), noop);

  pc.onicecandidate = function(event)
  {
   if (event && event.candidate && event.candidate.candidate)
   {
    var s = event.candidate.candidate.split('\n');
    ip.push(s[0].split(' ')[4]);
   }
  }
 }

 return ip;
}

Кроме того, имейте в виду, ребята, что это не что-то старое-новое, вроде CSS, border-radiusхотя один из тех битов, который полностью не поддерживается IE11 и старше. Всегда используйте обнаружение объектов, тестируйте в достаточно старых браузерах (например, Firefox 4, IE9, Opera 12.1) и убедитесь, что ваши новые скрипты не нарушают работу ваших новых фрагментов кода. Кроме того , всегда обнаружения совместимого со стандартами кода первыми , так что если есть что - то сказать с CSS префикса обнаружить стандартный не префикс код первой , а затем падает обратно , как в долгосрочной перспективе поддержки в конечном итоге будет стандартизирована для остальной части этого существования.

Джон
источник
вы повторно объявляете ip- строка 3 и строка 8.
user2757813
@Anu WebRTC не был представлен до Internet Explorer 15 (или «Edge 15»), поэтому нет. Вот почему в четвертой строке выше, если ни один из объектов не существует, функция вернет false. Если в IE есть другой способ добиться этого, то я пока о нем не знаю.
Джон
@John - как передать возвращаемое значение переменной php? Через скрытый пост?
MarcoZen
@MarcoZen В такой ситуации вы можете использовать <input name="example1" type="hidden" value="whatever" />или использовать AJAX POST. Я настоятельно рекомендую изучить мою ajax()функцию здесь: jabcreations.com/docs/javascript
John
Только что выяснилось, что некоторые браузеры (например, Chrome) теперь блокируют предоставление IP - тот же код теперь разрешается в имя хоста mDNS, если не запрашивается разрешение на видео / аудио. См. Groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger,
6

function getUserIP(onNewIP) { //  onNewIp - your listener function for new IPs
  //compatibility for firefox and chrome
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
  var pc = new myPeerConnection({
      iceServers: []
    }),
    noop = function() {},
    localIPs = {},
    ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
    key;

  function iterateIP(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  onNewIP
  //create a bogus data channel
  pc.createDataChannel("");

  // create offer and set local description
  pc.createOffer().then(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(iterateIP);
    });

    pc.setLocalDescription(sdp, noop, noop);
  }).catch(function(reason) {
    // An error occurred, so handle the failure to connect
  });

  //listen for candidate events
  pc.onicecandidate = function(ice) {
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(iterateIP);
  };
}
getUserIP(console.log)

Хоакин Пиньейро
источник
Пожалуйста, используйте параметры редактора, чтобы правильно отформатировать код.
31пий
3
Было бы здорово, если бы вы не только сбросили какой-нибудь код, но и объяснили, что происходит в его и вашем коде. Это помогает автору вопроса и другим пользователям. Хорошо, если это сработает, но, на мой взгляд, еще важнее знать, почему.
davejal
какие-нибудь IE совместимые решения?
Anu
1
Комментарий является копией этой статьи: ourcodeworld.com/articles/read/257/…
Darkshifty
Только что выяснилось, что некоторые браузеры (например, Chrome) теперь блокируют предоставление IP - тот же код теперь разрешается в имя хоста mDNS, если не запрашивается разрешение на видео / аудио. См. Groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger,
5

Chrome 76+

В прошлом году я использовал ответ Линблоу (19 октября 2018 г.), чтобы успешно обнаружить мой локальный IP-адрес через javascript. Однако недавние обновления Chrome (76?) Отказались от этого метода, и теперь он возвращает обфусцированный IP-адрес, например:1f4712db-ea17-4bcf-a596-105139dfd8bf.local

Если у вас есть полный контроль над своим браузером, вы можете отменить это поведение, отключив его в Chrome Flags, набрав это в адресной строке:

chrome://flags

и ОТКЛЮЧЕНИЕ флага Anonymize local IPs exposed by WebRTC

В моем случае мне нужен IP-адрес для сценария TamperMonkey, чтобы определять мое текущее местоположение и делать разные вещи в зависимости от моего местоположения. У меня также есть полный контроль над настройками моего браузера (без корпоративных политик и т. Д.). Так что для меня изменение chrome://flagsнастройки помогает.

Источники:

https://groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU

https://codelabs.developers.google.com/codelabs/webrtc-web/index.html

cssyphus
источник
этот флаг может исчезнуть. В настоящее время кажется, что расширения все еще получают IP-адреса, поэтому вы можете попытаться получить его из фонового скрипта. Однако в долгосрочной перспективе все ставки отменены.
Philipp Hancke
1
Согласно groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU , IP-адрес все равно должен быть возвращен, если вы реализуете свое решение, которое запрашивает разрешение на аудио / видео.
Christoph Bimminger
4

Дополнительную информацию о том, какие ограничения, скорее всего, добавят браузеры, чтобы смягчить это, и что IETF делает с этим, а также почему это необходимо, можно найти в IETF SPEC по обработке IP.

Каллен Пушистый Дженнингс
источник
0

RTCPeerConnectionМожет быть использован. В таких браузерах, как Chrome, где требуется getUserMediaразрешение , мы можем просто обнаружить доступные устройства ввода и запросить их.

const internalIp = async () => {
    if (!RTCPeerConnection) {
        throw new Error("Not supported.")
    }

    const peerConnection = new RTCPeerConnection({ iceServers: [] })

    peerConnection.createDataChannel('')
    peerConnection.createOffer(peerConnection.setLocalDescription.bind(peerConnection), () => { })

    peerConnection.addEventListener("icecandidateerror", (event) => {
        throw new Error(event.errorText)
    })

    return new Promise(async resolve => {
        peerConnection.addEventListener("icecandidate", async ({candidate}) => {
            peerConnection.close()

            if (candidate && candidate.candidate) {
                const result = candidate.candidate.split(" ")[4]
                if (result.endsWith(".local")) {
                    const inputDevices = await navigator.mediaDevices.enumerateDevices()
                    const inputDeviceTypes = inputDevices.map(({ kind }) => kind)

                    const constraints = {}

                    if (inputDeviceTypes.includes("audioinput")) {
                        constraints.audio = true
                    } else if (inputDeviceTypes.includes("videoinput")) {
                        constraints.video = true
                    } else {
                        throw new Error("An audio or video input device is required!")
                    }

                    const mediaStream = await navigator.mediaDevices.getUserMedia(constraints)
                    mediaStream.getTracks().forEach(track => track.stop())
                    resolve(internalIp())
                }
                resolve(result)
            }
        })
    })
}
Ричи Бендалл
источник