Pouvez-vous obtenir une adresse IP LAN locale d'utilisateurs via JavaScript?

102

Je sais que la réaction initiale à cette question est «non» et «cela ne peut pas être fait» et «vous ne devriez pas en avoir besoin, vous faites quelque chose de mal». Ce que j'essaie de faire, c'est d'obtenir l'adresse IP LAN des utilisateurs et de l'afficher sur la page Web. Pourquoi? Parce que c'est ce sur quoi je travaille, montrant autant d'informations que possible sur vous, le visiteur: http://www.whatsmyip.org/more-info-about-you/

Je ne fais donc rien avec l'adresse IP, à part la montrer à l'utilisateur à des fins d'information. J'avais l'habitude de faire cela en utilisant une petite applet Java. Cela a plutôt bien fonctionné. Mais ces jours-ci, le navigateur vous fait accepter et faire confiance tellement de fois, pour exécuter même l'applet java la plus mineure, que je préfère ne pas en lancer du tout.

Donc, pendant un moment, je viens de me débarrasser de cette fonctionnalité, mais j'aimerais qu'elle revienne si possible. C'était quelque chose que moi, en tant que consultant en informatique, j'utilisais de temps en temps. Il est plus rapide d'aller sur ce site Web pour voir sur quelle plage IP un réseau fonctionne, que d'aller dans Préférences système, Réseau, puis quelle que soit l'interface active.

Alors je me demande, en espérant, s'il existe un moyen de le faire en javascript seul? Peut-être que vous pouvez accéder à un nouvel objet, similaire à la façon dont javascript peut demander au navigateur où se trouve l'emplacement géographique sur Terre. Peut-être qu'il y a quelque chose de similaire pour les informations de réseau client? Sinon, peut-être existe-t-il une autre façon de le faire? Les seuls moyens auxquels je peux penser sont une applet java ou un objet flash. Je préfère ne faire ni l'un ni l'autre.

l008com
la source
1
Vous connaissez la réponse. Pourquoi demander alors? Il est peu probable que les applets Java ou les objets Flash soient autorisés par les utilisateurs (peut-être uniquement ceux qui sont nouveaux sur Internet) - ce n'est donc pas une solution dans un cas courant. ActiveX et les éléments à proximité ne fonctionnent que dans IE - et, par conséquent, les utilisateurs d'autres navigateurs ne seront pas affectés (et, de plus, même dans IE, il existe une politique de sécurité qui empêche le site Web de faire des choses désagréables)
Alma Do
Mon adresse IP est capturée HTTP_X_FORWARDED_FORsur cette page, dites simplement.
tomdemuyt
50
Pourquoi demander alors? Parce que peut-être, juste peut-être, je ne sais pas tout.
l008com
1
Ces gars le font: whatismyproxy.com
likebike
1
@likebike Belle. En regardant comment ils font cela.
Dominic Cerisano

Réponses:

117

En fait, la récente extension WebRTC de HTML5 permet à javascript d'interroger l'adresse IP du client local. Une preuve de concept est disponible ici: http://net.ipcalf.com

Cette fonctionnalité est apparemment par conception et n'est pas un bogue. Cependant, étant donné sa nature controversée, je serais prudent de me fier à ce comportement. Néanmoins, je pense que cela répond parfaitement et de manière appropriée à votre objectif (révéler à l'utilisateur ce que son navigateur fuit).

afourney
la source
1
Cela a été utile. Merci encore!
Ansuraj Khadanga
7
Il ne fonctionne que sur Chrome et Firefox, et PAS sur IE, Edge ou Safari
ali
Je cherchais mon IP WAN et ce site Web whatismyip.com m'a également donné mon adresse IP locale et je suppose que cela avait quelque chose à voir avec JS.
Shayan
@ali Vous avez raison, le site Web que j'ai mentionné ci-dessus n'est pas capable de dire mon adresse IP locale sur Edge.
Shayan
6
Google Chrome masque l'IP locale par défaut. Il montre quelque chose de similaire à e87e041d-15e1-4662-adad-7a6601fca9fb.local . Ce comportement peut être modifié en définissant la variable # enable-webrtc-hide-local-ips-with-mdns sur disabled dans Chrome: // flags
injaon
81

Mettre à jour

Cette solution ne fonctionnerait plus car les navigateurs corrigent la fuite de webrtc: pour plus d'informations à ce sujet, lisez cette autre question: RTCIceCandidate ne renvoie plus d'IP


En plus de la réponse de quatre personnes, ce code fonctionne dans les navigateurs prenant en charge WebRTC (Chrome et Firefox). J'ai entendu dire qu'il y avait un mouvement en cours pour implémenter une fonctionnalité qui oblige les sites à demander l'IP (comme dans le cas de la géolocalisation de l'utilisateur ou du média utilisateur) bien qu'elle n'ait pas encore été implémentée dans l'un de ces navigateurs.

Voici une version modifiée du code source , réduit les lignes, ne faisant aucune demande d'étourdissement puisque vous ne voulez que l'adresse IP locale et non l'adresse IP publique:

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

Nous créons une connexion d'homologue factice pour que l'homologue distant nous contacte. Nous échangeons généralement des candidats de glace entre eux et en lisant les candidats de glace, nous pouvons dire l'adresse IP de l'utilisateur.

Vous pouvez trouver une démo sur -> Démo

mido
la source
Merci pour ce Mido! Très appréciée.
Sujay Phadke
1
@dampee - Je pense qu'Edge ne prend pas en charge les canaux de données pour le moment.
MichaelB76
Qu'est-ce qu'un faux canal de données? Je ne trouve aucune référence sur google
AmazingTurtle
2
notez que l'API createOffer a basculé pour être basée sur Promise au lieu de successCallback et failCallback en tant que paramètres, donc cela peut ne pas fonctionner sur les versions plus récentes, voir: developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/…
Dickeylth
10

L' API WebRTC peut être utilisée pour récupérer l'adresse IP locale du client.

Cependant, le navigateur peut ne pas le prendre en charge ou le client peut l'avoir désactivé pour des raisons de sécurité. Dans tous les cas, il ne faut pas compter sur ce "hack" sur le long terme car il est susceptible d'être patché dans le futur (voir la réponse de Cullen Fluffy Jennings).

Le code ECMAScript 6 ci-dessous montre comment procéder.

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

Remarquez que j'écris return resolve(..)ou return reject(..)comme raccourci. Ces deux fonctions ne renvoient rien.

Ensuite, vous pouvez avoir quelque chose comme ça:

<!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
la source
9

J'ai nettoyé le message de mido, puis nettoyé la fonction qu'ils ont trouvée. Cela retournera falseou un fichier array. Lors du test rappelez -vous que vous avez besoin pour réduire le tableau dans la console développeur web sinon il est le comportement par défaut peut vous tromper non intuitif pour penser qu'il retourne un vide 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;
}

De plus, gardez à l'esprit que ce n'est pas quelque chose d'ancien-nouveau comme CSS, border-radiusmême si l'un de ces bits n'est carrément pas pris en charge par IE11 et les versions antérieures. Utilisez toujours la détection d'objet, testez dans des navigateurs raisonnablement plus anciens (par exemple Firefox 4, IE9, Opera 12.1) et assurez-vous que vos nouveaux scripts ne cassent pas vos nouveaux morceaux de code. De plus, détectez toujours le code conforme aux normes en premier, donc s'il y a quelque chose avec, par exemple, un préfixe CSS, détectez d' abord le code standard non préfixé , puis retombez car à long terme, le support sera finalement normalisé pour le reste de son existence.

John
la source
vous redéclarez ip- ligne 3 et ligne 8.
user2757813
@Anu WebRTC n'a pas été introduit avant Internet Explorer 15 (ou "Edge 15") donc non. C'est pourquoi sur la quatrième ligne ci-dessus, si aucun des objets n'existe, la fonction retournera false. S'il existe un autre moyen d'y parvenir dans IE, je n'en suis pas conscient pour le moment.
John
@John - comment passer la valeur de retour à une variable php? Via un message caché?
MarcoZen
@MarcoZen Vous pouvez utiliser <input name="example1" type="hidden" value="whatever" />ou utiliser un AJAX POST dans une telle situation. Je recommande fortement d'étudier ma ajax()fonction ici: jabcreations.com/docs/javascript
John
Je viens de découvrir que certains navigateurs (par exemple Chrome) bloquent maintenant la fourniture de l'adresse IP - le même code se résout maintenant en un nom d'hôte mDNS, si aucune autorisation vidéo / audio n'est demandée. Voir 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
la source
Veuillez utiliser les options de l'éditeur pour formater votre code de manière appropriée.
31piy
3
Ce serait formidable non seulement de laisser tomber du code, mais aussi de donner une explication de ce qui se passe dans le sien et dans votre code. Cela aide l'auteur de la question et les autres utilisateurs. C'est bien si ça marche, mais savoir pourquoi est encore plus important à mon avis.
davejal
des solutions compatibles IE?
Anu
1
Le commentaire est un copier-coller de cet article: ourcodeworld.com/articles/read/257/…
Darkshifty
Je viens de découvrir que certains navigateurs (par exemple Chrome) bloquent maintenant la fourniture de l'adresse IP - le même code se résout maintenant en un nom d'hôte mDNS, si aucune autorisation vidéo / audio n'est demandée. Voir groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger
5

Chrome 76+

L'année dernière, j'ai utilisé la réponse de Linblow (2018-oct-19) pour découvrir avec succès mon adresse IP locale via javascript. Cependant, les récentes mises à jour de Chrome (76?) Ont échoué cette méthode de sorte qu'elle renvoie maintenant une adresse IP obscurcie, telle que:1f4712db-ea17-4bcf-a596-105139dfd8bf.local

Si vous avez un contrôle total sur votre navigateur, vous pouvez annuler ce comportement en le désactivant dans Chrome Flags, en tapant ceci dans votre barre d'adresse:

chrome://flags

et DÉSACTIVER le drapeau Anonymize local IPs exposed by WebRTC

Dans mon cas, j'ai besoin de l'adresse IP d'un script TamperMonkey pour déterminer mon emplacement actuel et faire différentes choses en fonction de mon emplacement. J'ai également un contrôle total sur les paramètres de mon propre navigateur (pas de politique d'entreprise, etc.). Donc pour moi, changer le chrome://flagsréglage fait l'affaire.

Sources:

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

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

cssyphus
la source
ce drapeau pourrait disparaître. Il semble actuellement que les extensions obtiennent toujours des adresses IP, vous pouvez donc essayer de l'obtenir à partir du script d'arrière-plan. À long terme, tous les paris sont ouverts.
Philipp Hancke
1
Selon groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU , l'adresse IP doit toujours être renvoyée si vous implémentez votre solution qui demande une autorisation audio / vidéo.
Christoph Bimminger
4

Vous pouvez trouver plus d'informations sur les limitations que les navigateurs ajouteront probablement pour atténuer cela et ce que l'IETF fait à ce sujet, ainsi que les raisons pour lesquelles cela est nécessaire à l' IETF SPEC sur la gestion IP.

Cullen Fluffy Jennings
la source
0

Un RTCPeerConnectionpeut être utilisé. Dans les navigateurs comme Chrome où une getUserMediaautorisation est requise , nous pouvons simplement détecter les périphériques d'entrée disponibles et les demander.

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
la source