Czy możesz uzyskać adres IP lokalnej sieci LAN użytkownika za pomocą JavaScript?

102

Wiem, że początkowa reakcja na to pytanie brzmi „nie” i „nie da się tego zrobić” i „nie powinieneś tego potrzebować, robisz coś źle”. Próbuję uzyskać adres IP LAN użytkownika i wyświetlić go na stronie internetowej. Czemu? Bo o to właśnie chodzi na stronie, nad którą pracuję, pokazując jak najwięcej informacji o tobie, gościu: http://www.whatsmyip.org/more-info-about-you/

Więc tak naprawdę nie robię nic z adresem IP poza pokazaniem go użytkownikowi w celach informacyjnych. Robiłem to za pomocą małego apletu Java. Działało całkiem nieźle. Ale w dzisiejszych czasach przeglądarka sprawia, że ​​tak wiele razy zgadzasz się i ufasz, aby uruchomić nawet najmniejszy aplet java, że ​​wolałbym go w ogóle nie uruchamiać.

Więc na jakiś czas pozbyłem się tej funkcji, ale chciałbym, aby to z powrotem, jeśli to możliwe. To było coś, z czego ja, jako konsultant komputerowy, korzystałem od czasu do czasu. Szybciej jest przejść do tej witryny, aby zobaczyć, w jakim zakresie adresów IP działa sieć, niż przejść do Preferencji systemowych, Sieci, a następnie do dowolnego interfejsu.

Zastanawiam się więc, mam nadzieję, czy można to zrobić w samym javascript? Może jakiś nowy obiekt, do którego możesz uzyskać dostęp, podobnie jak javascript może zapytać przeglądarkę, gdzie znajduje się położenie geograficzne na Ziemi. Może jest coś podobnego do informacji o sieci klienta? Jeśli nie, może jest na to zupełnie inny sposób? Jedyne sposoby, które przychodzą mi do głowy, to aplet Java lub obiekt flash. Wolałbym nie robić żadnego z tych.

l008com
źródło
1
Znasz odpowiedź. Dlaczego więc pytasz? Jest mało prawdopodobne, aby aplety Java lub obiekty flash były dozwolone przez użytkowników (mogą to być tylko nowi w Internecie) - więc nie jest to typowe rozwiązanie. ActiveX i pobliskie rzeczy działają tylko w IE - a zatem użytkownicy innych przeglądarek nie zostaną dotknięci (a co więcej, nawet w IE istnieje polityka bezpieczeństwa, która zapobiega robieniu nieprzyjemnych rzeczy przez witrynę)
Alma Do
Mój adres IP jest przechwytywany HTTP_X_FORWARDED_FORna tej stronie, po prostu mówię.
tomdemuyt
51
Dlaczego więc pytasz? Bo może, tylko może, nie wiem wszystkiego.
l008com
1
Ci faceci to robią: whatismyproxy.com
likebike
1
@likebike Nice one. Patrząc, jak to robią.
Dominic Cerisano

Odpowiedzi:

117

Jak się okazuje, ostatnie rozszerzenie WebRTC w HTML5 pozwala javascript na wysyłanie zapytań do lokalnego adresu IP klienta. Dowód koncepcji jest dostępny tutaj: http://net.ipcalf.com

Ta funkcja jest najwyraźniej zgodna z projektem i nie jest błędem. Jednak biorąc pod uwagę jego kontrowersyjny charakter, byłbym ostrożny, jeśli chodzi o poleganie na tym zachowaniu. Niemniej jednak uważam, że doskonale i odpowiednio adresuje twój zamierzony cel (ujawnia użytkownikowi, co przecieka jego przeglądarka).

podróż
źródło
1
To było pomocne. Dzięki jeszcze raz!
Ansuraj Khadanga
7
Działa tylko na chrome i firefox, a NIE na IE, Edge czy safari
ali
Szukałem mojego WAN IP i ta strona whatismyip.com dała mi również moje lokalne IP i myślę, że miało to coś wspólnego z JS.
Shayan,
@ali Masz rację, wspomniana powyżej witryna nie jest w stanie podać mojego lokalnego adresu IP na Edge.
Shayan,
7
Google Chrome domyślnie ukrywa lokalny adres IP. Pokazuje coś podobnego do e87e041d-15e1-4662-adad-7a6601fca9fb.local . To zachowanie można zmienić, ustawiając zmienną # enable-webrtc-hide-local-ips-with-mdns na wyłączoną w Chrome: // flags
injaon
81

Aktualizacja

To rozwiązanie nie działałoby już, ponieważ przeglądarki naprawiają wyciek webrtc: aby uzyskać więcej informacji na ten temat, przeczytaj to drugie pytanie: RTCIceCandidate nie zwraca już adresu IP


Oprócz odpowiedzi udzielonej przez firmę, ten kod działa w przeglądarkach obsługujących WebRTC (Chrome i Firefox). Słyszałem, że ma miejsce ruch mający na celu zaimplementowanie funkcji, która sprawia, że ​​witryny żądają adresu IP (jak w przypadku lokalizacji geograficznej użytkownika lub mediów użytkownika), chociaż nie została jeszcze zaimplementowana w żadnej z tych przeglądarek.

Oto zmodyfikowana wersja kodu źródłowego , zmniejszona liczba wierszy, nie wysyłająca żadnych żądań ogłuszenia, ponieważ chcesz tylko lokalnego adresu IP, a nie publicznego adresu 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;
 }
};

Tworzymy fikcyjne połączenie równorzędne, aby zdalny peer mógł się z nami skontaktować. Zwykle wymieniamy się kandydatami na lód i czytając kandydatów na lód, możemy podać adres IP użytkownika.

Możesz znaleźć demo na -> Demo

mido
źródło
Dzięki za to Mido! Bardzo cenione.
Sujay Phadke
1
@dampee - Uważam, że Edge nie obsługuje obecnie kanałów danych.
MichaelB76
Co to jest fałszywy kanał danych? Nie można znaleźć żadnego odniesienia w Google
AmazingTurtle
2
zauważ, że interfejs API createOffer został przełączony na oparty na Promise zamiast SuccessCallback i failCallback jako paramach, więc może to nie działać w nowszych wersjach, zobacz: developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ ...
Dickeylth
10

WebRTC API może być używany do pobierania klienta lokalnego IP.

Jednak przeglądarka może go nie obsługiwać lub klient mógł ją wyłączyć ze względów bezpieczeństwa. W każdym razie nie należy polegać na tym „hacku” w dłuższej perspektywie, ponieważ prawdopodobnie zostanie on załatany w przyszłości (patrz odpowiedź Cullen Fluffy Jennings).

Poniższy kod ECMAScript 6 pokazuje, jak to zrobić.

/* 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();
    };
} );

Zauważ, że piszę return resolve(..)lub return reject(..)jako skrót. Obie te funkcje nic nie zwracają.

Wtedy możesz mieć coś takiego:

<!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
źródło
9

Wyczyściłem post mido, a następnie wyczyściłem znalezioną funkcję. Spowoduje to powrót falselub array. Podczas testowania pamiętaj , że musisz zwinąć tablicę w konsoli programisty, w przeciwnym razie jej nieintuicyjne domyślne zachowanie może skłonić Cię do myślenia, że ​​zwraca wartość pustą 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;
}

Dodatkowo, pamiętajcie, że nie jest to coś starego i nowego, jak CSS, border-radiuschociaż jeden z tych bitów nie jest obsługiwany przez IE11 i starsze. Zawsze używaj wykrywania obiektów, testuj w rozsądnie starszych przeglądarkach (np. Firefox 4, IE9, Opera 12.1) i upewnij się, że nowsze skrypty nie psują nowszych fragmentów kodu. Dodatkowo zawsze wykryć standardów kodu zgodnego pierwszy więc jeśli jest coś z CSS powiedzieć prefiks wykryć standardowy kod nie uprzednio zamontowany pierwszy , a potem cofać jak w dłuższej perspektywie wsparcia zostaną ostatecznie ujednolicone dla reszty jest to istnienia.

Jan
źródło
ponownie deklarujesz ip- linia 3 i linia 8.
user2757813
@Anu WebRTC został wprowadzony dopiero w Internet Explorerze 15 (lub „Edge 15”), więc nie. Dlatego w czwartej linii powyżej, jeśli żaden z obiektów nie istnieje, funkcja zwróci false. Jeśli istnieje inny sposób osiągnięcia tego w IE, to w tej chwili nie jestem tego świadomy.
Jan
@John - jak przekazujemy zwracaną wartość do zmiennej php? Za pośrednictwem ukrytego wpisu?
MarcoZen
@MarcoZen W <input name="example1" type="hidden" value="whatever" />takiej sytuacji możesz użyć lub użyć AJAX POST. Gorąco polecam przestudiowanie mojej ajax()funkcji tutaj: jabcreations.com/docs/javascript
John
Właśnie dowiedziałem się, że niektóre przeglądarki (np. Chrome) blokują teraz dostarczanie adresu IP - ten sam kod jest teraz zastępowany nazwą hosta mDNS, jeśli nie jest wymagane zezwolenie na wideo / audio. Zobacz 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)

Joaquín Piñeyro
źródło
Użyj opcji edytora, aby odpowiednio sformatować kod.
lipca
3
Byłoby wspaniale, gdybyś nie tylko upuścił kod, ale także wyjaśnił, co się dzieje w jego i twoim kodzie. Pomaga autorowi pytania i innym użytkownikom. Dobrze, jeśli to działa, ale wiedza dlaczego jest moim zdaniem jeszcze ważniejsza.
davejal
jakieś rozwiązania zgodne z IE?
Anu
1
Komentarz jest wklejoną
Darkshifty
Właśnie dowiedziałem się, że niektóre przeglądarki (np. Chrome) blokują teraz dostarczanie adresu IP - ten sam kod jest teraz zastępowany nazwą hosta mDNS, jeśli nie jest wymagane zezwolenie na wideo / audio. Zobacz groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger,
5

Chrome 76+

W zeszłym roku użyłem odpowiedzi Linblow (2018-październik-19), aby pomyślnie odkryć mój lokalny adres IP za pomocą javascript. Jednak ostatnie aktualizacje Chrome (76?) Sprawiły, że ta metoda nie została rozwiązana i zwraca teraz zaciemniony adres IP, taki jak:1f4712db-ea17-4bcf-a596-105139dfd8bf.local

Jeśli masz pełną kontrolę nad swoją przeglądarką, możesz cofnąć to zachowanie, wyłączając je we flagach Chrome, wpisując to w pasku adresu:

chrome://flags

i WYŁĄCZENIE flagi Anonymize local IPs exposed by WebRTC

W moim przypadku potrzebuję adresu IP dla skryptu TamperMonkey, aby określić moją obecną lokalizację i robić różne rzeczy w zależności od mojej lokalizacji. Mam również pełną kontrolę nad własnymi ustawieniami przeglądarki (brak Zasad korporacyjnych itp.). Więc dla mnie zmiana chrome://flagsustawienia załatwia sprawę.

Źródła:

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

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

cssyphus
źródło
ta flaga może zniknąć. Obecnie wydaje się, że rozszerzenia nadal otrzymują adresy IP, więc możesz spróbować pobrać je ze skryptu działającego w tle. Jednak długoterminowe wszystkie zakłady są wyłączone.
Philipp Hancke,
1
Według groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU adres IP powinien nadal zostać zwrócony, jeśli zaimplementujesz swoje rozwiązanie, które żąda pozwolenia na dźwięk / wideo.
Christoph Bimminger,
4

Więcej informacji na temat ograniczeń, jakie przeglądarki prawdopodobnie dodadzą, aby to złagodzić i co robi w tej sprawie IETF, a także dlaczego jest to potrzebne w IETF SPEC w zakresie obsługi IP

Cullen Fluffy Jennings
źródło
0

RTCPeerConnectionMogą być użyte. W przeglądarkach takich jak Chrome, w których getUserMediawymagane jest pozwolenie , możemy po prostu wykryć dostępne urządzenia wejściowe i poprosić o nie.

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)
            }
        })
    })
}
Richie Bendall
źródło