不使用API,JS利用WebRTC获取客户端IP的方法

作者:窗边听雨

时间:2026-11-03

浏览:650

详细讲解在不调用任何第三方HTTP API的情况下,使用纯JavaScript与WebRTC的RTCPeerConnection接口获取客户端IP地址的具体原理、完整实现代码、mDNS隐私机制与工程避坑指南。

文章目录
技术教程 前端网络 WebRTC 原理
不使用API,JS利用WebRTC获取客户端IP的方法
图 0:WebRTC RTCPeerConnection 客户端 IP 探测整体架构概览

前端页面需要获取客户端 IP 地址时,最常见的做法是向第三方 HTTP 服务(如 ipify 或自建的定位接口)发起请求并读取返回结果。然而,通过浏览器内置的 WebRTC API,网页可以在不发起任何 HTTP API 请求的前提下,直接探知本机的网络候选信息(ICE Candidate),并从中解析出客户端的公网出口 IP 或本地内网地址。本文将详细讲解这一技术的底层工作机制、标准 JavaScript 实现代码、现代浏览器的 mDNS 隐私保护现状以及工程应用中的边界与限制。

一、WebRTC 获取客户端 IP 的底层原理

WebRTC(Web Real-Time Communication)是现代浏览器原生支持的实时音视频与数据传输标准。在建立点对点(P2P)连接时,双方必须先获知彼此的网络通信路径,这个流程在技术上称为 ICE(Interactive Connectivity Establishment,交互式连接建立)。

为了探寻可达路径,浏览器底层的 WebRTC 引擎会通过 RTCPeerConnection 接口主动收集多种候选网络地址(ICE Candidates),主要分为以下两类:

  • Host Candidate(本机候选):由系统物理或虚拟网卡直接派生,代表设备在本地局域网中的真实 IP(例如 192.168.1.100 或 IPv6 链路本地地址)。
  • Server Reflexive Candidate(srflx,服务端反射候选):当指定了 STUN(Session Traversal Utilities for NAT)服务器时,浏览器会向其发送极其轻量的 UDP 探测报文。STUN 服务器识别出报文在公网出口的源 IP 与端口,并在响应中原样返回。浏览器由此探明自己在 NAT 网关之外的公网 IP。

在这个通信过程中,STUN 使用的是基于 UDP 的标准二进制穿透协议(RFC 5389 / RFC 8489),而不是上层的 HTTP RESTful API。只要在 RTCPeerConnection 中挂载 onicecandidate 监听器,JavaScript 便可在事件对象中截获包含 IP 地址的文本行,从而达到“不调用任何 API 即可读取客户端 IP”的效果。

不使用API,JS利用WebRTC获取客户端IP的方法
图 1:WebRTC ICE 候选收集与 STUN 网络发现架构示意图

二、核心操作步骤与代码实现

实现该方法不需要引入任何第三方库,依靠浏览器原生对象 RTCPeerConnection 即可。核心操作分为四个阶段:

  1. 创建对等连接实例与数据通道:初始化 RTCPeerConnection 并提供公共 STUN 服务器地址。必须同时调用 createDataChannel('') 创建一个空的虚拟数据通道,否则引擎在没有媒体流和通道时不会触发候选收集。
  2. 注册候选监听器与解析器:通过 pc.onicecandidate 接收底层触发的候选事件,利用正则表达式提取出候选文本中的 IP 字符串。
  3. 生成 Offer 触发网络探测:调用 pc.createOffer() 生成本地 SDP 描述,并执行 pc.setLocalDescription(offer)。这一步是通知协议栈开始执行网络扫描的关键“发令枪”。
  4. 捕获地址并清理释放资源:成功解析出合法 IP 后,立即调用 pc.close() 断开连接,避免占用端口与内存。
不使用API,JS利用WebRTC获取客户端IP的方法
图 2:JS 利用 WebRTC 获取客户端 IP 的 4 步执行流程

完整且可直接运行的 JavaScript 封装示例

下面是一段封装为 Promise 的实用代码,支持设置超时保护并过滤掉无效字符:

/**
 * 利用 WebRTC 原生能力探测客户端 IP
 * @param {number} timeoutMs - 超时时间(毫秒),默认为 3000ms
 * @returns {Promise<string>} 解析到的客户端 IP 地址
 */
function getClientIPViaWebRTC(timeoutMs = 3000) {
  return new Promise((resolve, reject) => {
    // 1. 检查浏览器是否支持 RTCPeerConnection
    const RTCPC = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
    if (!RTCPC) {
      return reject(new Error('当前浏览器环境不支持 WebRTC'));
    }

    // 2. 配置公开 STUN 服务器(用于生成公网 srflx 候选)
    const config = {
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:global.stun.twilio.com:3478' }
      ]
    };

    const pc = new RTCPC(config);
    let hasResolved = false;

    // 资源清理函数
    const cleanup = () => {
      try {
        pc.onicecandidate = null;
        pc.close();
      } catch (e) {
        // 忽略已关闭异常
      }
    };

    // 3. 必须创建数据通道,驱动浏览器开启 ICE 收集流程
    pc.createDataChannel('');

    // 4. 监听 ICE 候选事件
    pc.onicecandidate = (event) => {
      if (!event || !event.candidate || !event.candidate.candidate) {
        return;
      }

      const candidateStr = event.candidate.candidate;

      // 匹配 IPv4 正则
      const ipRegex = /([0-9](1, 3)(\.[0-9](1, 3)){3})/;
      const match = ipRegex.exec(candidateStr);

      if (match && match[1]) {
        const ip = match[1];

        // 过滤常见的本地保留无效地址
        if (ip !== '0.0.0.0' && !ip.startsWith('127.')) {
          hasResolved = true;
          cleanup();
          resolve(ip);
        }
      }
    };

    // 5. 创建 Offer 并设置本地描述,触发网络握手发令枪
    pc.createOffer()
      .then((offer) => pc.setLocalDescription(offer))
      .catch((err) => {
        cleanup();
        reject(err);
      });

    // 6. 超时保护机制
    setTimeout(() => {
      if (!hasResolved) {
        cleanup();
        reject(new Error('WebRTC 获取 IP 超时,可能处于纯局域网或 UDP 端口被阻断'));
      }
    }, timeoutMs);
  });
}

// 示例调用
getClientIPViaWebRTC()
  .then((ip) => {
    console.log('成功捕获客户端 IP:', ip);
  })
  .catch((err) => {
    console.warn('捕获失败:', err.message);
  });

三、现代浏览器的 mDNS 隐私保护机制

在早期的 WebRTC 规范实现中(约 2018 年以前),网页无需向用户申请任何权限,只要执行上述代码就可以直接枚举出用户设备连接的所有真实局域网 IP(例如家庭 WiFi 分配的 192.168.1.15、公司办公网分配的 10.8.x.x)。这导致了严重的用户设备追踪和内网指纹泄露隐患。

为了解决该隐私漏洞,现代主流浏览器(Chrome 75+、Firefox 68+、Safari 等)全面引入了 mDNS(Multicast DNS,多播 DNS,RFC 6762) 保护机制:

  • 默认遮蔽真实内网 IP:当网页在未授权麦克风或摄像头权限的情况下收集 Host Candidate 时,浏览器会生成一个随机生成的 UUID,并将其拼接为 .local 格式的本地域名(如 e6284f2b-7c50-482a-a920-55fa11e64917.local)输出在候选字符串中,完全屏蔽了底层的 192.168.x.x。
  • 公网 IP(srflx)依然可正常解析:如果配置了有效的公共 STUN 服务器,生成的候选类型为 typ srflx。因为公网出口是由外部 STUN 服务器经由 UDP 响应回显的,不受本机的 mDNS 策略掩盖,因此公网 IP 依然能够被 JavaScript 稳定捕获。
  • 媒体权限特例:如果当前网页已经获得了用户的多媒体设备权限(通过 navigator.mediaDevices.getUserMedia 获得授权),浏览器才会解除 mDNS 限制,在 Host Candidate 中暴露真实的局域网物理 IP。
不使用API,JS利用WebRTC获取客户端IP的方法
图 3:现代浏览器对 WebRTC 候选类型的隐私决策流程

四、方案对比、工程边界与最佳实践

采用 WebRTC 方式探测 IP,虽然摆脱了对外部付费或限频 HTTP API 的依赖,但在实际工程环境中依然存在不可忽视的技术边界。

不使用API,JS利用WebRTC获取客户端IP的方法
图 4:WebRTC 原生方案与第三方 HTTP API 方案全方位对比矩阵
关键限制 1:UDP 传输依赖
STUN 协议工作在 UDP 基础之上(默认端口 19302 或 3478)。如果用户的网络环境处于严格受限的企业级防火墙、代理服务器之后,或者网络策略禁止了所有出站 UDP 数据包,则 STUN 探测将无法完成,onicecandidate 事件中不会出现 srflx 类型的候选。
关键限制 2:不能替代服务端鉴权
通过浏览器端 JavaScript 提取到的 IP 属于客户端前台运行数据,容易被本地调试工具拦截篡改。如果系统需要进行安全审计、风控拦截或真实身份认证,必须以服务端的 HTTP 连接套接字(或 Nginx/反向代理层传来的 X-Forwarded-For)所记录的远程 IP 为准,绝不可信任客户端前台回传的值。
生产实践建议:
1. 配置双 STUN 容灾:避免使用单一的 STUN 域名,建议同时配置 Google 与 Twilio 等多个高可用公共 STUN 节点。
2. IPv6 与 IPv4 兼容:在支持 IPv6 的移动蜂窝或教育网环境下,STUN 返回的可能为标准的 IPv6 地址。如果业务场景需要 IPv6,需要相应扩展正则表达式;如果仅需 IPv4,应在正则中对点分十进制格式进行精确限定。
3. 生命周期及时关闭:获取到候选后,切勿遗漏 pc.close()。未关闭的连接会导致底层套接字句柄持续驻留,造成页面性能与内存损耗。

五、总结

不使用第三方 HTTP API,利用 WebRTC 获取客户端 IP 是一种利用浏览器原生网络协议栈特性的高阶技巧。其核心在于利用 RTCPeerConnection 触发底层 ICE 收集引擎,并通过公共 STUN 服务的 UDP 报文反射解析出客户端的外网出口 IP。理解 mDNS 的保护机制与 UDP 穿透特性,有助于开发者在无依赖网络排障、前端连通性诊断等特定场景下,合理且安全地使用这项技术。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章