Bạn có thể có được người dùng địa chỉ IP mạng LAN cục bộ thông qua JavaScript không?


102

Tôi biết phản ứng ban đầu cho câu hỏi này là "không" và "nó không thể được thực hiện" và "bạn không nên cần nó, bạn đang làm điều gì đó sai". Những gì tôi đang cố gắng làm là lấy địa chỉ IP LAN của người dùng và hiển thị nó trên trang web. Tại sao? Bởi vì đó là trang mà tôi đang làm việc, hiển thị càng nhiều thông tin càng tốt về bạn, khách truy cập: http://www.whatsmyip.org/more-info-about-you/

Vì vậy, tôi không thực sự LÀM bất cứ điều gì với IP, ngoài việc hiển thị nó cho người dùng vì mục đích thông tin. Tôi đã từng làm điều này bằng cách sử dụng một ứng dụng nhỏ Java. Nó hoạt động khá tốt. Nhưng ngày nay, trình duyệt khiến bạn phải đồng ý và tin tưởng rất nhiều lần, để chạy ngay cả ứng dụng java nhỏ nhất, mà tôi không muốn chạy một chút nào.

Vì vậy, trong một thời gian, tôi vừa loại bỏ tính năng này, nhưng tôi muốn nó trở lại nếu có thể. Đó là thứ mà tôi, với tư cách là một nhà tư vấn máy tính, thực sự sẽ sử dụng đôi khi. Truy cập trang web này để xem mạng đang chạy trên dải IP nào sẽ nhanh hơn là vào System Preferences, Networking và sau đó bất kỳ giao diện nào đang hoạt động.

Vì vậy, tôi tự hỏi, hy vọng, nếu có một số cách để làm điều đó một mình trong javascript? Có thể một số đối tượng mới mà bạn có thể truy cập, tương tự như cách javascript có thể hỏi trình duyệt vị trí địa lý trên trái đất ở đâu. Có thể có một cái gì đó tương tự cho thông tin mạng khách hàng? Nếu không, có lẽ có một số cách khác hoàn toàn để làm điều đó? Những cách duy nhất tôi có thể nghĩ đến là một applet java hoặc một đối tượng flash. Tôi không muốn làm một trong hai điều đó.


1
Bạn biết câu trả lời. Tại sao lại hỏi? Các ứng dụng Java hoặc các đối tượng flash khó có thể được người dùng cho phép (có thể chỉ dành cho những người mới sử dụng Internet) - vì vậy nó không phải là một giải pháp trong trường hợp phổ biến. ActiveX và những thứ lân cận chỉ hoạt động trong IE - và do đó, người dùng các trình duyệt khác sẽ không bị ảnh hưởng (và hơn thế nữa, ngay cả trong IE cũng có một chính sách bảo mật ngăn trang web làm những việc xấu)
Alma Do

Địa chỉ IP của tôi được ghi lại HTTP_X_FORWARDED_FORtrên trang đó, chỉ cần sayin`.
tomdemuyt

50
Tại sao lại hỏi? Bởi vì có thể, chỉ có thể, tôi không biết tất cả.
l008com

1
Những kẻ làm điều đó: whatismyproxy.com
likebike

1
@likebike Đẹp quá. Nhìn vào cách họ đang làm điều này.
Dominic Cerisano

Câu trả lời:


117

Hóa ra, phần mở rộng WebRTC gần đây của HTML5 cho phép javascript truy vấn địa chỉ IP của máy khách cục bộ. Một bằng chứng về khái niệm có sẵn tại đây: http://net.ipcalf.com

Tính năng này rõ ràng là do thiết kế và không phải là một lỗi. Tuy nhiên, với bản chất gây tranh cãi của nó, tôi sẽ thận trọng khi dựa vào hành vi này. Tuy nhiên, tôi nghĩ rằng nó giải quyết một cách hoàn hảo và phù hợp mục đích dự định của bạn (tiết lộ cho người dùng biết trình duyệt của họ đang bị rò rỉ gì).


1
Điều này rất hữu ích. Cảm ơn một lần nữa!
Ansuraj Khadanga

7
Nó chỉ hoạt động trên chrome và firefox, KHÔNG hoạt động trên IE, Edge hoặc safari
ali

Tôi đang tra cứu IP WAN của mình và trang web whatismyip.com này cũng cấp cho tôi IP cục bộ của tôi và tôi đoán nó có liên quan gì đó đến JS.
Shayan

@ali Bạn nói đúng, trang web tôi đã đề cập ở trên không thể cho biết IP cục bộ của tôi trên Edge.
Shayan

6
Google Chrome đang ẩn IP cục bộ theo mặc định. Nó hiển thị một cái gì đó tương tự như e87e041d-15e1-4662-adad-7a6601fca9fb.local . Bạn có thể thay đổi hành vi này bằng cách đặt biến # enable-webrtc-hide-local-ips-with-mdns thành tắt trong Chrome: // flags
invaon 11/09/19

81

Cập nhật

Giải pháp này sẽ không hoạt động nữa vì trình duyệt đang sửa lỗi rò rỉ webrtc: để biết thêm thông tin, hãy đọc câu hỏi khác này: RTCIceCandidate không còn trả lại IP


Ngoài câu trả lời của afourney, mã này hoạt động trong các trình duyệt hỗ trợ WebRTC (Chrome và Firefox). Tôi nghe nói rằng có một phong trào đang diễn ra để triển khai một tính năng khiến các trang web yêu cầu IP (như trong trường hợp vị trí địa lý của người dùng hoặc phương tiện người dùng) mặc dù nó vẫn chưa được triển khai trên một trong hai trình duyệt đó.

Đây là phiên bản đã sửa đổi của mã nguồn , giảm bớt các dòng, không đưa ra bất kỳ yêu cầu choáng váng nào vì bạn chỉ muốn Local IP không phải IP Public:

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

Chúng tôi đang tạo một kết nối ngang hàng giả để đồng đẳng từ xa liên hệ với chúng tôi. Chúng tôi thường trao đổi các ứng viên băng với nhau và đọc các ứng viên băng mà chúng tôi có thể cho biết ip của người dùng.

Bạn có thể tìm thấy bản demo tại -> Demo


Cảm ơn vì Mido này! Nhiều đánh giá cao.
Sujay Phadke,

1
@dampee - Tôi tin rằng Edge hiện không hỗ trợ các kênh dữ liệu.
MichaelB76

Kênh dữ liệu không có thật là gì? Không thể tìm thấy bất kỳ tài liệu tham khảo nào trên google
AmazingTurtle

2
lưu ý rằng api createOffer đã chuyển sang dựa trên Promise thay vì successCallback và failCallback làm tham số, vì vậy điều này có thể không hoạt động trên các phiên bản mới hơn, hãy xem: developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/…
Dickeylth

10

Các API WebRTC có thể được sử dụng để lấy IP cục bộ của khách hàng.

Tuy nhiên, trình duyệt có thể không hỗ trợ nó hoặc khách hàng có thể đã tắt nó vì lý do bảo mật. Trong mọi trường hợp, không nên dựa vào "bản hack" này về lâu dài vì nó có khả năng được vá trong tương lai (xem câu trả lời của Cullen Fluffy Jennings).

Mã ECMAScript 6 dưới đây trình bày cách thực hiện điều đó.

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

Chú ý tôi viết return resolve(..)hoặc return reject(..)như một phím tắt. Cả hai hàm đó không trả về bất cứ điều gì.

Sau đó, bạn có thể có một cái gì đó sau:

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

Tôi đã dọn dẹp bài đăng của mido và sau đó dọn dẹp chức năng mà họ tìm thấy. Điều này sẽ trở lại falsehoặc một array. Khi kiểm tra, hãy nhớ rằng bạn cần thu gọn mảng trong bảng điều khiển dành cho nhà phát triển web, nếu không, hành vi mặc định không trực quan có thể đánh lừa bạn nghĩ rằng nó đang trả về một khoảng trống 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;
}

Ngoài ra, xin lưu ý với mọi người rằng đây không phải là thứ gì đó cũ mới như CSS border-radiusmặc dù một trong những bit hoàn toàn không được IE11 trở lên hỗ trợ. Luôn sử dụng tính năng phát hiện đối tượng, kiểm tra trong các trình duyệt cũ hợp lý (ví dụ: Firefox 4, IE9, Opera 12.1) và đảm bảo rằng các tập lệnh mới hơn của bạn không phá vỡ các đoạn mã mới hơn của bạn. Thêm vào đó luôn luôn phát hiện các tiêu chuẩn mã phù hợp đầu tiên vì vậy nếu có cái gì đó với nói một CSS tiền tố phát hiện mã phi tiền tố tiêu chuẩn đầu tiên và sau đó rơi trở lại như trong sự hỗ trợ lâu dài cuối cùng sẽ được chuẩn hóa cho phần còn lại của nó tồn tại.


bạn đang khai báo lại ip- dòng 3 và dòng 8.
user2757813

@Anu WebRTC đã không được giới thiệu cho đến Internet Explorer 15 (hoặc "Edge 15") nên không. Đó là lý do tại sao ở dòng thứ tư ở trên nếu không có đối tượng nào tồn tại thì hàm sẽ trả về false. Nếu có cách khác để đạt được điều này trong IE thì tôi không biết về nó tại thời điểm này.
John

@John - làm cách nào để chúng ta chuyển giá trị trả về cho một biến php? Qua một bài viết ẩn?
MarcoZen

@MarcoZen Bạn có thể sử dụng <input name="example1" type="hidden" value="whatever" />hoặc sử dụng AJAX POST trong trường hợp như vậy. Tôi khuyên bạn nên nghiên cứu của tôi ajax()chức năng ở đây: jabcreations.com/docs/javascript
John

Chỉ phát hiện ra rằng một số trình duyệt (ví dụ: Chrome) hiện chặn cung cấp mã IP - cùng một mã hiện phân giải thành tên máy chủ mDNS, nếu không có quyền Video / Âm thanh được yêu cầu. Xem 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)


Vui lòng sử dụng các tùy chọn trình chỉnh sửa để định dạng mã của bạn một cách thích hợp.
31piy

3
Sẽ thật tuyệt nếu bạn không chỉ thả một số mã mà còn đưa ra lời giải thích về những gì đang diễn ra trong mã của anh ấy và của bạn. Nó giúp tác giả câu hỏi và những người dùng khác. Thật tốt nếu nó hoạt động, nhưng biết tại sao theo quan điểm của tôi thậm chí còn quan trọng hơn.
davejal 14/03/17

bất kỳ giải pháp tương thích với IE?
Anu

1
Nhận xét là bản sao của bài viết này: ourcodeworld.com/articles/read/257/…
Darkshifty

Chỉ phát hiện ra rằng một số trình duyệt (ví dụ: Chrome) hiện chặn cung cấp mã IP - cùng một mã hiện phân giải thành tên máy chủ mDNS, nếu không có quyền Video / Âm thanh được yêu cầu. Xem groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger

5

Chrome 76+

Năm ngoái, tôi đã sử dụng câu trả lời của Linblow (2018 -10-19) để khám phá thành công IP cục bộ của tôi thông qua javascript. Tuy nhiên, các bản cập nhật Chrome gần đây (76?) Đã sử dụng phương pháp này để bây giờ nó trả về một IP bị xáo trộn, chẳng hạn như:1f4712db-ea17-4bcf-a596-105139dfd8bf.local

Nếu bạn có toàn quyền kiểm soát trình duyệt của mình, bạn có thể hoàn tác hành vi này bằng cách tắt nó trong Chrome Flags, bằng cách nhập nó vào thanh địa chỉ của bạn:

chrome://flags

và BẬT cờ Anonymize local IPs exposed by WebRTC

Trong trường hợp của tôi, tôi yêu cầu IP cho tập lệnh TamperMonkey để xác định vị trí hiện tại của tôi và thực hiện những việc khác dựa trên vị trí của tôi. Tôi cũng có toàn quyền kiểm soát cài đặt trình duyệt của riêng mình (không có Chính sách công ty, v.v.). Vì vậy, đối với tôi, thay đổi chrome://flagscài đặt không phải là mẹo nhỏ.

Nguồn:

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

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


lá cờ đó có thể biến mất. Hiện tại, có vẻ như các tiện ích mở rộng vẫn nhận được IP vì vậy bạn có thể cố gắng lấy nó từ tập lệnh nền. Trong dài hạn, tất cả các cược đều tắt.
Philipp Hancke

1
Theo groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU , IP sẽ vẫn được trả lại nếu bạn triển khai giải pháp mà nó yêu cầu quyền Âm thanh / Video.
Christoph Bimminger

4

Bạn có thể tìm thêm thông tin về những hạn chế mà trình duyệt có thể sẽ thêm vào để giảm thiểu điều này và những gì IETF đang làm với nó cũng như lý do tại sao điều này là cần thiết tại IETF SPEC về xử lý IP


0

An RTCPeerConnectioncó thể được sử dụng. Trong các trình duyệt như Chrome, nơi cần có getUserMediaquyền , chúng tôi chỉ có thể phát hiện các thiết bị đầu vào có sẵn và yêu cầu chúng.

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)
            }
        })
    })
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.