คุณสามารถรับที่อยู่ IP LAN ของผู้ใช้ผ่าน JavaScript ได้หรือไม่?


102

ฉันรู้ว่าปฏิกิริยาเริ่มต้นของคำถามนี้คือ "ไม่" และ "ไม่สามารถทำได้" และ "คุณไม่ควรต้องการมันคุณกำลังทำอะไรผิดพลาด" สิ่งที่ฉันพยายามทำคือรับที่อยู่ IP LAN ของผู้ใช้และแสดงบนหน้าเว็บ ทำไม? เพราะนั่นคือหน้าเว็บที่ฉันกำลังทำอยู่โดยแสดงข้อมูลเกี่ยวกับคุณให้มากที่สุดเท่าที่จะเป็นไปได้ผู้เยี่ยมชม: http://www.whatsmyip.org/more-info-about-you/

ดังนั้นฉันจึงไม่ได้ทำอะไรกับ IP จริง ๆ นอกจากแสดงให้ผู้ใช้เห็นเพื่อจุดประสงค์ในการให้ข้อมูล ฉันเคยทำสิ่งนี้โดยใช้ Java applet ขนาดเล็ก มันทำงานได้ดี แต่ทุกวันนี้เบราว์เซอร์ทำให้คุณเห็นด้วยและไว้วางใจหลายครั้งเพื่อเรียกใช้แม้แต่แอพเพล็ตจาวาที่เล็กที่สุดซึ่งฉันไม่ควรเรียกใช้เลย

ในขณะที่ฉันเพิ่งกำจัดฟีเจอร์นี้ แต่ฉันต้องการมันกลับมาถ้าเป็นไปได้ เป็นสิ่งที่ฉันในฐานะที่ปรึกษาคอมพิวเตอร์จะใช้เป็นครั้งคราว การไปที่เว็บไซต์นี้จะเร็วกว่าเพื่อดูว่าเครือข่ายกำลังทำงานอยู่ในช่วง IP ใดมากกว่าที่จะไปที่การตั้งค่าระบบเครือข่ายและจากนั้นอินเทอร์เฟซใดก็ตามที่ใช้งานอยู่

ดังนั้นฉันจึงสงสัยและหวังว่าจะมีวิธีทำด้วยจาวาสคริปต์เพียงอย่างเดียวหรือไม่? บางทีวัตถุใหม่ ๆ ที่คุณสามารถเข้าถึงได้คล้ายกับวิธีที่จาวาสคริปต์สามารถถามเบราว์เซอร์ว่าตำแหน่งทางภูมิศาสตร์บนโลกอยู่ที่ไหน อาจมีบางอย่างที่คล้ายกันสำหรับข้อมูลเครือข่ายไคลเอนต์? ถ้าไม่อาจมีวิธีอื่นที่จะทำทั้งหมด? วิธีเดียวที่ฉันคิดได้คือ java applet หรือวัตถุแฟลช ฉันไม่อยากทำอย่างใดอย่างหนึ่ง


1
คุณรู้คำตอบ. ถามแล้วทำไม? แอพเพล็ต Java หรือวัตถุแฟลชไม่น่าจะได้รับอนุญาตจากผู้ใช้ (อาจเป็นได้เฉพาะผู้ที่ยังใหม่ในอินเทอร์เน็ต) ดังนั้นจึงไม่ใช่วิธีแก้ปัญหาในกรณีทั่วไป ActiveX และสิ่งที่อยู่ใกล้เคียงใช้งานได้เฉพาะใน IE - ดังนั้นผู้ใช้เบราว์เซอร์อื่น ๆ จะไม่ได้รับผลกระทบ (และยิ่งใน IE มีนโยบายความปลอดภัยที่ป้องกันไม่ให้เว็บไซต์ทำสิ่งที่น่ารังเกียจ)
Alma Do

ที่อยู่ IP ของฉันถูกจับผ่านHTTP_X_FORWARDED_FORในหน้านั้นเพียงแค่พูดว่า "
tomdemuyt

50
ถามแล้วทำไม? เพราะบางทีฉันก็ไม่รู้ทุกอย่าง
l008com

1
พวกเหล่านี้ทำมัน whatismyproxy.com
likebike

1
@likebike ดีที่หนึ่ง. ดูว่าพวกเขากำลังทำสิ่งนี้อย่างไร
Dominic Cerisano

คำตอบ:


117

ปรากฎว่าส่วนขยาย WebRTC ล่าสุดของ HTML5 ช่วยให้จาวาสคริปต์สามารถสืบค้นที่อยู่ IP ของไคลเอ็นต์ภายในเครื่องได้ สามารถดูหลักฐานแนวคิดได้ที่นี่: http://net.ipcalf.com

เห็นได้ชัดว่าคุณลักษณะนี้เกิดจากการออกแบบและไม่ใช่ข้อบกพร่อง อย่างไรก็ตามด้วยลักษณะที่ขัดแย้งกันฉันจึงควรระมัดระวังในการใช้พฤติกรรมนี้ อย่างไรก็ตามฉันคิดว่ามันตอบสนองจุดประสงค์ของคุณได้อย่างสมบูรณ์และเหมาะสม (เปิดเผยให้ผู้ใช้เห็นว่าเบราว์เซอร์ของพวกเขารั่วไหล)


1
นี่เป็นประโยชน์ ขอบคุณอีกครั้ง!
Ansuraj Khadanga

7
มันเป็นเพียงแค่การทำงานบนโครเมี่ยมและ Firefox และไม่ได้อยู่ใน IE, EDGE หรือซาฟารี
ali

ฉันกำลังค้นหา WAN IP ของฉันและเว็บไซต์whatismyip.comนี้ก็ให้ IP ในพื้นที่ของฉันด้วยและฉันเดาว่ามันเกี่ยวข้องกับ JS
Shayan

@ali คุณถูกต้องเว็บไซต์ที่ฉันกล่าวถึงข้างต้นไม่สามารถบอก IP ในเครื่องของฉันบน Edge ได้
Shayan

6
Google Chrome ซ่อน IP ในเครื่องโดยค่าเริ่มต้น มันแสดงให้เห็นสิ่งที่คล้ายกับe87e041d-15e1-4662-Adad-7a6601fca9fb.local พฤติกรรมนี้อาจมีการเปลี่ยนแปลงโดยการตั้งค่าตัวแปร# enable-WebRTC ซ่อนท้องถิ่น-IPS ที่มี mDNSเพื่อคนพิการใน Chrome: // ธง
injaon

81

อัปเดต

โซลูชันนี้จะใช้งานไม่ได้อีกต่อไปเนื่องจากเบราว์เซอร์กำลังแก้ไขการรั่วไหลของ webrtc: สำหรับข้อมูลเพิ่มเติมโปรดอ่านคำถามอื่น ๆ นี้: RTCIceCandidate ไม่ส่งคืน IP อีกต่อไป


นอกเหนือจากคำตอบของ afourney แล้วรหัสนี้ยังทำงานในเบราว์เซอร์ที่รองรับ WebRTC (Chrome และ Firefox) ฉันได้ยินมาว่ามีความเคลื่อนไหวในการใช้งานคุณลักษณะที่ทำให้ไซต์ร้องขอ IP (เช่นในกรณีของตำแหน่งทางภูมิศาสตร์ของผู้ใช้หรือสื่อของผู้ใช้) แม้ว่าจะยังไม่ได้ใช้งานในเบราว์เซอร์ใดก็ตาม

นี่คือเวอร์ชันที่แก้ไขของซอร์สโค้ดลดบรรทัดไม่ทำการร้องขอการทำให้มึนงงใด ๆ เนื่องจากคุณต้องการเพียง Local IP ไม่ใช่ Public 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 ของผู้ใช้ได้

คุณสามารถดูการสาธิตได้ที่ -> Demo


ขอบคุณสำหรับ Mido นี้! ชื่นชมมาก
Sujay Phadke

1
@dampee - ฉันเชื่อว่า Edge ไม่รองรับช่องข้อมูลในขณะนี้
MichaelB76

ช่องข้อมูลปลอมคืออะไร? ไม่พบข้อมูลอ้างอิงใด ๆ ใน Google
AmazingTurtle

2
โปรดทราบว่า API ของ createOffer ได้เปลี่ยนมาใช้ Promise แทนที่จะเป็น successCallback และ failCallback เป็น params ดังนั้นสิ่งนี้อาจใช้ไม่ได้กับเวอร์ชันที่ใหม่กว่าโปรดดูที่developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/…
Dickeylth

10

WebRTC APIสามารถนำมาใช้เพื่อดึงท้องถิ่น IP ของลูกค้า

อย่างไรก็ตามเบราว์เซอร์อาจไม่รองรับหรือไคลเอนต์อาจปิดใช้งานด้วยเหตุผลด้านความปลอดภัย ไม่ว่าในกรณีใดเราไม่ควรพึ่งพา "แฮ็ก" นี้ในระยะยาวเนื่องจากมีแนวโน้มที่จะได้รับการแก้ไขในอนาคต (ดูคำตอบของ Cullen Fluffy Jennings)

โค้ด 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>

9

ฉันทำความสะอาดโพสต์ของมิโดแล้วก็ทำความสะอาดฟังก์ชันที่พวกเขาพบ นี้อย่างใดอย่างหนึ่งจะกลับมาหรือfalse arrayเมื่อทำการทดสอบจำไว้ว่าคุณจะต้องยุบอาร์เรย์ในคอนโซลนักพัฒนาเว็บมิฉะนั้นมันเป็นพฤติกรรมเริ่มต้น nonintuitive 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") จึงไม่มี นั่นคือเหตุผลที่ในบรรทัดที่สี่ด้านบนหากไม่มีวัตถุใดอยู่ฟังก์ชันจะส่งคืนเท็จ หากมีวิธีอื่นในการบรรลุสิ่งนี้ใน 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)


โปรดใช้ตัวเลือกตัวแก้ไขเพื่อจัดรูปแบบโค้ดของคุณอย่างเหมาะสม
31piy

3
จะดีมากถ้าคุณไม่เพียงแค่วางโค้ดบางส่วนเท่านั้น แต่ยังให้คำอธิบายว่าเกิดอะไรขึ้นในรหัสของเขาและของคุณด้วย ช่วยให้ผู้เขียนคำถามและผู้ใช้คนอื่น ๆ มันดีถ้ามันได้ผล แต่การรู้ว่าทำไมถึงสำคัญกว่าในความคิดของฉัน
davejal

โซลูชันใด ๆ ที่เข้ากันได้กับ IE?
อนุ

1
ความคิดเห็นเป็นสำเนาของบทความนี้: ourcodeworld.com/articles/read/257/…
Darkshifty

เพิ่งพบว่าบางเบราว์เซอร์ (เช่น Chrome) บล็อกการให้ IP - รหัสเดียวกันในขณะนี้แก้ไขเป็นชื่อโฮสต์ mDNS แล้วหากไม่มีการขออนุญาตวิดีโอ / เสียง ดูgroups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger

5

Chrome 76+

ปีที่แล้วฉันใช้คำตอบของ Linblow (2018-19 ต.ค. ) เพื่อค้นหา IP ในเครื่องของฉันผ่านทางจาวาสคริปต์ได้สำเร็จ อย่างไรก็ตามการอัปเดต 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


ธงนั้นอาจหายไป ขณะนี้ดูเหมือนว่าส่วนขยายยังคงได้รับ 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)
            }
        })
    })
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.