Bisakah Anda Mendapatkan Alamat IP LAN Lokal Pengguna Melalui JavaScript?

102

Saya tahu reaksi awal untuk pertanyaan ini adalah "tidak" dan "itu tidak dapat dilakukan" dan "Anda seharusnya tidak membutuhkannya, Anda melakukan sesuatu yang salah". Apa yang saya coba lakukan adalah mendapatkan alamat IP LAN pengguna, dan menampilkannya di halaman web. Mengapa? Karena itulah halaman yang sedang saya kerjakan, yang menampilkan informasi sebanyak mungkin tentang Anda, pengunjung: http://www.whatsmyip.org/more-info-about-you/

Jadi saya sebenarnya tidak MELAKUKAN apa pun dengan IP tersebut, selain menunjukkannya kepada pengguna untuk tujuan informasional. Saya biasa melakukan ini dengan menggunakan applet Java kecil. Ini bekerja dengan cukup baik. Namun belakangan ini, browser membuat Anda berkali-kali setuju dan percaya, untuk menjalankan bahkan applet java yang paling kecil, yang saya lebih suka tidak menjalankannya sama sekali.

Jadi untuk sementara saya baru saja menyingkirkan fitur ini, tetapi saya ingin mengembalikannya jika memungkinkan. Itu adalah sesuatu yang saya, sebagai konsultan komputer, akan benar-benar gunakan dari waktu ke waktu. Lebih cepat membuka situs web ini untuk melihat rentang IP apa yang dijalankan jaringan, daripada masuk ke System Preferences, Networking, lalu antarmuka apa pun yang aktif.

Jadi saya bertanya-tanya, berharap, apakah ada cara untuk melakukannya dengan javascript saja? Mungkin beberapa objek baru dapat Anda akses, mirip dengan cara javascript dapat menanyakan browser di mana letak geografis di bumi. Mungkin ada sesuatu yang serupa untuk informasi jaringan klien? Jika tidak, mungkin ada cara lain untuk sepenuhnya melakukannya? Satu-satunya cara yang dapat saya pikirkan adalah applet java, atau objek flash. Saya lebih suka tidak melakukan keduanya.

l008com.dll
sumber
1
Kamu tahu jawabannya. Lalu mengapa bertanya? Applet Java atau objek flash kemungkinan tidak diizinkan oleh pengguna (mungkin hanya oleh mereka yang baru di Internet) - jadi ini bukan solusi dalam kasus umum. ActiveX dan item di sekitarnya hanya berfungsi di IE - dan, oleh karena itu, pengguna browser lain tidak akan terpengaruh (dan, terlebih lagi, bahkan di IE ada kebijakan keamanan yang mencegah situs web melakukan hal-hal buruk)
Alma Do
Alamat IP saya ditangkap melalui HTTP_X_FORWARDED_FORhalaman itu, katakan saja.
tomdemuyt
50
Lalu mengapa bertanya? Karena mungkin, mungkin saja, saya tidak tahu segalanya.
l008com
1
Orang-orang ini melakukannya: whatismyproxy.com
likebike
1
@likebike Bagus. Melihat bagaimana mereka melakukan ini.
Dominic Cerisano

Jawaban:

117

Ternyata, ekstensi WebRTC terbaru dari HTML5 memungkinkan javascript untuk menanyakan alamat IP klien lokal. Bukti konsep tersedia di sini: http://net.ipcalf.com

Fitur ini tampaknya merupakan desain , dan bukan bug. Namun, mengingat sifatnya yang kontroversial, saya akan berhati-hati dalam mengandalkan perilaku ini. Namun demikian, saya pikir ini dengan sempurna dan tepat menangani tujuan yang Anda maksudkan (mengungkapkan kepada pengguna apa yang dibocorkan browser mereka).

perjalanan
sumber
1
Ini sangat membantu. Terima kasih lagi!
Ansuraj Khadanga
7
Ini hanya berfungsi di chrome dan firefox, Dan BUKAN di IE, Edge atau safari
ali
Saya mencari IP WAN saya dan situs web ini whatismyip.com juga memberi saya IP lokal saya dan saya kira itu ada hubungannya dengan JS.
Shayan
@ali Anda benar, situs web yang saya sebutkan di atas tidak dapat memberi tahu IP lokal saya di Edge.
Shayan
6
Google Chrome menyembunyikan IP lokal secara default. Ini menunjukkan sesuatu yang mirip dengan e87e041d-15e1-4662-adad-7a6601fca9fb.local . Perilaku ini dapat diubah dengan menyetel variabel # enable-webrtc-hide-local-ips-with-mdns untuk dinonaktifkan di Chrome: // flags
injaon
81

Memperbarui

Solusi ini tidak akan berfungsi lagi karena browser memperbaiki kebocoran webrtc: untuk info lebih lanjut tentang membaca pertanyaan lain ini: RTCIceCandidate tidak lagi mengembalikan IP


Selain jawaban perjalanan, kode ini berfungsi di browser yang mendukung WebRTC (Chrome dan Firefox). Saya mendengar ada gerakan yang sedang terjadi untuk mengimplementasikan fitur yang membuat situs meminta IP (seperti dalam kasus geo-location pengguna atau user-media) meskipun itu belum diterapkan di salah satu browser tersebut.

Berikut adalah versi modifikasi dari kode sumber , mengurangi baris, tidak membuat permintaan setrum karena Anda hanya menginginkan IP Lokal bukan IP Publik:

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

Kami membuat koneksi peer tiruan untuk peer jarak jauh untuk menghubungi kami. Kami biasanya bertukar kandidat es satu sama lain dan membaca kandidat es kami dapat mengetahui ip pengguna.

Anda dapat menemukan demo di -> Demo

mido
sumber
Terima kasih untuk Mido ini! Sangat dihargai.
Sujay Phadke
1
@dampee - Saya yakin Edge tidak mendukung saluran data saat ini.
MichaelB76
Apa itu saluran data palsu? Tidak dapat menemukan referensi apa pun di google
AmazingTurtle
2
perhatikan api createOffer telah beralih menjadi berdasarkan Promise, bukan pada successCallback dan failCallback sebagai params, jadi ini mungkin tidak berfungsi pada versi yang lebih baru, lihat: developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/…
Dickeylth
10

The WebRTC API dapat digunakan untuk mengambil IP lokal klien.

Namun browser mungkin tidak mendukungnya, atau klien mungkin telah menonaktifkannya untuk alasan keamanan. Bagaimanapun, seseorang tidak boleh mengandalkan "peretasan" ini dalam jangka panjang karena kemungkinan akan ditambal di masa mendatang (lihat jawaban Cullen Fluffy Jennings).

Kode ECMAScript 6 di bawah ini menunjukkan bagaimana melakukan itu.

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

Perhatikan saya menulis return resolve(..)atau return reject(..)sebagai jalan pintas. Kedua fungsi tersebut tidak mengembalikan apapun.

Maka Anda mungkin memiliki sesuatu ini:

<!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
sumber
9

Saya membersihkan pos mido dan kemudian membersihkan fungsi yang mereka temukan. Ini akan kembali falseatau array. Saat menguji, ingatlah bahwa Anda perlu menciutkan larik di konsol pengembang web, jika tidak, perilaku default yang tidak intuitif dapat menipu Anda untuk berpikir bahwa array mengembalikan kosong 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;
}

Selain itu, harap diingat orang-orang bahwa ini bukan sesuatu yang lama-baru seperti CSS border-radiusmeskipun salah satu bit yang sama sekali tidak didukung oleh IE11 dan yang lebih lama. Selalu gunakan deteksi objek, uji di browser yang cukup lama (mis. Firefox 4, IE9, Opera 12.1) dan pastikan skrip terbaru Anda tidak merusak bit kode baru Anda. Selain itu selalu mendeteksi kode yang sesuai standar terlebih dahulu jadi jika ada sesuatu dengan mengatakan awalan CSS mendeteksi kode non-prefiks standar terlebih dahulu dan kemudian kembali karena dalam dukungan jangka panjang pada akhirnya akan distandarisasi untuk sisa keberadaannya.

John
sumber
Anda mendeklarasikan ulang ip- baris 3 dan baris 8.
pengguna2757813
@Anu WebRTC tidak diperkenalkan hingga Internet Explorer 15 (atau "Edge 15") jadi tidak. Itulah mengapa pada baris keempat di atas jika tidak ada objek yang ada, fungsi akan mengembalikan nilai salah. Jika ada cara lain untuk mencapai ini di IE, maka saya tidak menyadarinya saat ini.
Yohanes
@ John - bagaimana kita meneruskan nilai kembali ke variabel php? Melalui postingan tersembunyi?
MarcoZen
@MarcoZen Anda dapat menggunakan <input name="example1" type="hidden" value="whatever" />atau menggunakan AJAX POST dalam situasi seperti ini. Saya sangat merekomendasikan mempelajari ajax()fungsi saya di sini: jabcreations.com/docs/javascript
John
Baru saja mengetahui bahwa beberapa browser (misalnya Chrome) sekarang memblokir penyediaan IP - kode yang sama sekarang diselesaikan ke nama host mDNS, jika tidak ada izin Video / Audio yang diminta. Lihat 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
sumber
Harap gunakan opsi editor untuk memformat kode Anda dengan tepat.
31piy
3
Akan sangat bagus jika Anda tidak hanya memberikan beberapa kode, tetapi juga memberikan penjelasan tentang apa yang terjadi di dalam dirinya dan kode Anda. Ini membantu penulis pertanyaan dan pengguna lain. Itu bagus jika berhasil, tetapi mengetahui mengapa itu lebih penting menurut saya.
davejal
ada solusi yang kompatibel dengan IE?
Anu
1
Komentar tersebut adalah salinan tempel artikel ini: ourcodeworld.com/articles/read/257/…
Darkshifty
Baru saja mengetahui bahwa beberapa browser (misalnya Chrome) sekarang memblokir penyediaan IP - kode yang sama sekarang diselesaikan ke nama host mDNS, jika tidak ada izin Video / Audio yang diminta. Lihat groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger
5

Chrome 76+

Tahun lalu saya menggunakan jawaban Linblow (2018-Okt-19) untuk berhasil menemukan IP lokal saya melalui javascript. Namun, pembaruan Chrome terkini (76?) Telah mengubah metode ini sehingga sekarang mengembalikan IP yang dikaburkan, seperti:1f4712db-ea17-4bcf-a596-105139dfd8bf.local

Jika Anda memiliki kendali penuh atas browser Anda, Anda dapat membatalkan perilaku ini dengan mematikannya di Bendera Chrome, dengan mengetik ini di bilah alamat Anda:

chrome://flags

dan MENONAKTIFKAN bendera Anonymize local IPs exposed by WebRTC

Dalam kasus saya, saya memerlukan IP untuk skrip TamperMonkey untuk menentukan lokasi saya saat ini dan melakukan hal yang berbeda berdasarkan lokasi saya. Saya juga memiliki kendali penuh atas pengaturan browser saya sendiri (tidak ada Kebijakan Perusahaan, dll). Jadi bagi saya, mengubah chrome://flagspengaturan itu berhasil.

Sumber:

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

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

cssyphus
sumber
bendera itu mungkin akan hilang. Saat ini sepertinya ekstensi masih mendapatkan IP sehingga Anda dapat mencoba mendapatkannya dari skrip latar belakang. Untuk jangka panjang, semua taruhan dibatalkan.
Philipp Hancke
1
Menurut groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU , IP harus tetap dikembalikan jika Anda menerapkan solusi yang meminta izin Audio / Video.
Christoph Bimminger
4

Anda dapat menemukan info lebih lanjut tentang batasan apa yang mungkin akan ditambahkan browser untuk mengurangi ini dan apa yang dilakukan IETF tentangnya serta mengapa ini diperlukan di IETF SPEC tentang penanganan IP

Cullen Fluffy Jennings
sumber
0

Sebuah RTCPeerConnectionbisa digunakan. Di browser seperti Chrome yang memerlukan getUserMediaizin , kami hanya dapat mendeteksi perangkat input yang tersedia dan memintanya.

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
sumber