WebRTC如何获取用户真实IP
没太深入细节,仅仅当作科普看看了。
原理简述
WebRTC参考:给好奇者的WebRTC,这个文档看前三节就可以理解为什么可以用WebRTC获取真实IP了。
WebRTC要实现的是P2P的传输,如果通信的两端在一个网络还好说,但我们目的肯定是要让世界上任意两个终端通信。
要让世界上不同内网的内网相互通信,用到的方法就是NAT。假如我要和远在伦敦的J先生通信的话,我就要拿到NAT为我这次通信提供的映射地址,这个映射地址是公网上的。然后在于J先生建立通信的时候,我把这个映射地址给他作为我的目标地址。
STUN协议就是配合NAT来实现这个过程的,它可以让用户拿到映射地址。这个过程如下:
- 用户向STUN服务器发送一个
STUN Binding Request - STUN服务器回应一个
STUN Binding Response - 该
STUN Binding Response将包含映射地址

为什么说这个IP会比较“真实”,是因为STUN协议用的是UDP,而大部分代理工具只代理了TCP,这就导致了真实IP的泄露。作为一个安全狗,拿到这个IP地址之后WebRTC对我就可以说是结束了,至于什么ICE、TURN、信令了解下就可以。
代码实现
方法实现参考:WebRTC疑似可以获取你的真实IP地址。
获取IP的核心代码其实就下面一块:
// 定义STUN服务器的配置,这里使用了Google的公共STUN服务器
const iceServers = [
{ urls: 'stun:stun.l.google.com:19302' }
];
// 函数用于获取用户的公网IP地址
function getUserIPs(callback) {
// 创建一个新的RTCPeerConnection实例,传入STUN服务器配置
const myPeerConnection = new RTCPeerConnection({ iceServers });
// 创建一个数据通道,这在此例中主要是为了触发ICE过程
myPeerConnection.createDataChannel("");
// 创建一个SDP offer,启动WebRTC协商过程
myPeerConnection.createOffer().then(offer =>
// 将生成的offer设置为本地描述
myPeerConnection.setLocalDescription(offer)
);
// 当发现新的ICE候选时触发此事件处理函数
myPeerConnection.onicecandidate = function(event) {
// 检查是否存在候选信息
if (event.candidate) {
// 从ICE候选信息中提取IP地址
// ICE候选信息格式通常是 "candidate:<id> <generation> <transport> <priority> <ip> <port> typ <type> ..."
const parts = event.candidate.candidate.split(' ');
const ip = parts[4]; // IP地址通常位于分割后的第五个元素
// 使用回调函数返回找到的IP地址
callback(ip);
}
};
}
完整代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Get IP Addresses</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.6/lib/theme-chalk/index.css">
<script src="https://unpkg.com/vue@2.6.14"></script>
<script src="https://unpkg.com/element-ui@2.15.6/lib/index.js"></script>
</head>
<body>
<div id="app">
<el-container>
<el-header>
<h1>Your IP Addresses:</h1>
</el-header>
<el-main>
<el-table :data="ipAddresses" style="width: 100%" border>
<el-table-column prop="type" label="Type" width="180"></el-table-column>
<el-table-column prop="address" label="IP Address"></el-table-column>
</el-table>
</el-main>
</el-container>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
ipAddresses: [
{ type: 'XFF or CDN IP Address', address: 'Loading...' },
{ type: 'WebRTC Local IP Address', address: 'Loading...' },
{ type: 'WebRTC IPv4 Address', address: 'Loading...' },
{ type: 'WebRTC IPv6 Address', address: 'Loading...' }
]
};
},
mounted() {
// Fetch XFF or CDN IP address
const xhr = new XMLHttpRequest();
xhr.open('GET', './get_real_ip.php', true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
const jsonResponse = JSON.parse(xhr.responseText);
this.ipAddresses[0].address = jsonResponse.ip;
}
};
xhr.send();
// WebRTC IPs
const iceServers = [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
{ urls: 'stun:stun2.l.google.com:19302' },
{ urls: 'stun:stun3.l.google.com:19302' },
{ urls: 'stun:stun4.l.google.com:19302' },
];
// getUserIPs function
function getUserIPs(callback) {
const myPeerConnection = new RTCPeerConnection({ iceServers });
myPeerConnection.createDataChannel("");
myPeerConnection.createOffer().then(offer => myPeerConnection.setLocalDescription(offer));
myPeerConnection.onicecandidate = function(event) {
if (event.candidate) {
const parts = event.candidate.candidate.split(' ');
const ip = parts[4];
callback(ip);
}
};
}
getUserIPs((ip) => {
const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}(([0-9a-fA-F]{1,4}:){1,4}|((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
if (ipv4Regex.test(ip)) {
this.ipAddresses[2].address = ip;
} else if (ipv6Regex.test(ip)) {
this.ipAddresses[3].address = ip;
} else {
this.ipAddresses[1].address = ip;
}
});
}
});
</script>
</body>
</html>
get_real_ip.php
<?php
function getUserIP() {
$ip = '';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$ip = getUserIP();
echo json_encode(['ip' => $ip]);
?>
效果

参考链接
https://zhuanlan.zhihu.com/p/623495107?utm_id=0
https://webrtcforthecurious.com/zh/
https://scz.617.cn/web/202304131224.txt
https://michaelyou.github.io/2018/08/01/%E7%9C%9F%E5%AE%9E%E4%B8%96%E7%95%8C%E4%B8%AD%E7%9A%84WebRTC/