Skip to content

WebRTC 会泄露真实 IP 吗?怎么检测和防护(前端 JS 实现) ​

先给结论:会,而且你开着代理也挡不住。 浏览器里的 WebRTC 为了建立 P2P 连接,会主动向 STUN 服务器暴露你的真实公网 IP,这个「暴露」绕过了系统代理,VPN 开着也一样漏。网上那些「WebRTC 泄漏测试」工具站,测的就是这个。对做风控、做内容平台的人来说,这事反过来是个机会:用户挂代理伪装归属地,WebRTC 却可能把真实位置抖出来。

一、WebRTC 为什么会「漏」 ​

WebRTC 是浏览器里的音视频实时通信能力,视频通话、在线会议、网页连麦都是它。它有一个叫 ICE 的机制:为了让两个人在不同的网络环境里也能直连,浏览器会主动收集自己所有的网络地址(本地地址、公网地址、通过 STUN 服务器探测到的映射地址),这堆地址叫 ICE candidate,拿来和对方交换。

问题就出在「主动收集公网地址」这一步。浏览器会尝试用 UDP 直连 STUN 服务器(比如 Google 的 stun.l.google.com:19302),STUN 服务器一看:「哟,你的公网 IP 是这个」,就记下来了。这个行为发生在浏览器底层,用的不是 HTTP 代理链路,所以系统代理、浏览器代理、甚至大多数 VPN 客户端都管不着它——你的真实公网 IP 就这么被浏览器自己说出来了。

Chrome 后来的版本默认用 mDNS 混淆(candidate 里显示成 xxx.local),但这只是把「局域网 IP」藏了,公网 IP 照样能从 STUN 服务器拿到,只是获取方式稍微绕一点。Firefox 默认则可以直接读出公网地址。

二、检测:一个页面脚本搞定 ​

写一个页面,用原生 RTCPeerConnection 强行触发 ICE 收集,把 candidate 里的 IP 地址捞出来过滤出公网 IPv4。下面是完整可运行的检测脚本:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>WebRTC 泄露检测</title>
</head>
<body>
<h3>WebRTC 真实 IP 检测</h3>
<p>点击按钮,看浏览器是否泄露公网 IP:</p>
<button onclick="detect()">开始检测</button>
<pre id="result">等待检测…</pre>

<script>
// 用原生 WebRTC 收集 ICE candidate,提取其中的 IP 地址
async function getWebRTCIPs() {
  return new Promise((resolve) => {
    const ips = [];
    const pc = new RTCPeerConnection({
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun1.l.google.com:19302' }
      ]
    });
    // 创建数据通道,强制触发 ICE 流程(纯音频/视频对等连接也一样)
    pc.createDataChannel('probe');
    pc.onicecandidate = (e) => {
      if (!e.candidate) {
        pc.close();
        resolve(ips);
        return;
      }
      const c = e.candidate.candidate;
      // candidate 形如:candidate:... 1 udp 2113937151 1.2.3.4 54321 typ srflx
      const m = c.match(/(\d{1,3}(?:\.\d{1,3}){3})/);
      if (m && !ips.includes(m[1])) {
        ips.push(m[1]);
      }
    };
    // 30 秒兜底,防止某些浏览器不触发 onicecandidate
    setTimeout(() => { pc.close(); resolve(ips); }, 30000);
  });
}

async function detect() {
  const out = document.getElementById('result');
  out.textContent = '正在检测…';
  const ips = await getWebRTCIPs();
  out.textContent = '检测到的 IP 列表:\n' + (ips.length ? ips.join('\n') : '(没测到公网 IP,可能被 mDNS 混淆或浏览器限制)');
}
</script>
</body>
</html>

存成 html 直接双击打开(需要挂代理/VPN 的情况下测,效果最明显:列表里那个不在你代理出口 IP 段里的,就是被 WebRTC 漏出来的真实 IP)。检测到 IP 后,把每个 IP 贴进 https://ip9.com.cn/get?ip=xxx 查归属地,能看到它们各自的省市和运营商。

三、风控视角:WebRTC「帮」你抓伪装用户 ​

如果只当个测试工具,那这事没多大价值。真正有用的是反着用:用户口口声声在国内,IP 归属地也显示在国内,但他的 WebRTC 漏出的 IP 归属地在国外——对不上号。

具体做法三步:

  1. 页面加载时用上面脚本收集 WebRTC IP 列表;
  2. 同一个接口请求里带上用户正常访问的出口 IP(服务端拿到的源 IP);
  3. 后端把 WebRTC IP 和出口 IP 分别查归属地,比对国家和省份。

这里有个细节:WebRTC 在浏览器里是「可能泄露」,不是「必然泄露」,而且伪装用户也可能直接禁掉 WebRTC。所以正确的用法是当辅助信号,配合其他维度用。单独一个 WebRTC 信号不能定罪,但它能大幅提升对可疑账号的怀疑权重。放在前端做的是「收集」,放在后端做的是「比对」——把收集到的 IP 列表 POST 给接口,后端统一查归属地。

四、JS 后端比对示例(Node.js) ​

前端收集到 IP 后,交给 Node 后端比对(生产环境建议对收集接口加频控,防止被刷):

javascript
// compare.js —— 比对「声称的出口 IP」与「WebRTC 泄露 IP」的归属地
// 用法:node compare.js <出口IP> <WebRTC IP1> [WebRTC IP2...]
const https = require('https');

function queryGeo(ip) {
  // ip9.com.cn 免费版:get?ip= 查指定 IP,返回国家/省市/运营商
  return new Promise((resolve) => {
    https.get(`https://ip9.com.cn/get?ip=${encodeURIComponent(ip)}`, (res) => {
      let body = '';
      res.on('data', (c) => (body += c));
      res.on('end', () => {
        try {
          const d = JSON.parse(body);
          resolve(d.data || null);
        } catch (e) {
          resolve(null);
        }
      });
    }).on('error', () => resolve(null));
  });
}

async function main() {
  const exitIP = process.argv[2];
  const webRTCIPs = process.argv.slice(3);
  if (!exitIP || webRTCIPs.length === 0) {
    console.log('用法: node compare.js <出口IP> <WebRTC IP1> [IP2...]');
    return;
  }

  const exitGeo = await queryGeo(exitIP);
  console.log(`出口 IP  ${exitIP} → ${exitGeo ? exitGeo.country + exitGeo.prov + exitGeo.city : '查询失败'}`);

  for (const ip of webRTCIPs) {
    if (ip === exitIP) continue; // 与出口一致,正常
    const geo = await queryGeo(ip);
    if (!geo) continue;
    const flag = (geo.country !== exitGeo.country) ? '⚠️ 跨国不一致' : '正常';
    console.log(`WebRTC  ${ip} → ${geo.country + geo.prov + geo.city}  ${flag}`);
  }
}

main();

node compare.js 61.140.0.1 202.4.12.3,输出里出现「⚠️ 跨国不一致」,就是典型的代理伪装线索:出口 IP 是中国宽带,WebRTC 漏出的却是海外 IP。免费版 60 次/分钟/IP 的额度,做逐条比对完全够;如果要做全量实时比对且量很大,再看 VIP。

五、落地注意事项 ​

  • 误报率要认:公司网络、学校网络下,多个用户可能共享同一出口 IP,WebRTC 和出口 IP 对不上不一定是伪装。信号要打分制,别用一刀切。
  • mDNS 影响:Chrome 下 candidate 可能先给 .local,在上面脚本里就会被过滤掉,测不到。这时换 Firefox 或让用户允许站点使用本地网络权限,能提高捕获率。
  • 合规:收集用户 WebRTC 地址涉及隐私,页面要提示用途;只把「比对结果」落库而不是存原始 IP,审计压力小很多。
  • 用户侧怎么防:真要在意泄露,Firefox 里 about:config 关掉 media.peerconnection.enabled,Chrome 装 WebRTC 控制类插件,或全局走支持 UDP 代理的客户端(Clash 的 TUN 模式能覆盖 WebRTC 流量)。

总结 ​

WebRTC 泄露不是洪水猛兽,对工具站是「防」,对风控是「用」。检测脚本十行就能跑起来,配合 IP 归属地接口做出口 IP 与真实 IP 的比对,识别代理伪装账号的成本很低。接口就是 https://ip9.com.cn/get?ip=xxx,免费版单 IP 查询、60 次/分钟、无需注册,字段含国家、省市、运营商、经纬度,够用。想了解免费版和 VIP 版(区县、ip_type、ASN)的完整功能差异,去官网 https://www.ip9.com.cn 看功能清单。