WebRTC(网页实时通信技术)是一系列为了建立端到端文本或者随机数据的规范,标准,API和概念的统称。这些对等端通常是由两个浏览器组成,但是WebRTC也可以被用于在客户端和服务器之间建立通信连接,或者在任何其他可以实施WebRTC标准的设备之间进行通信建立。简单来讲,webRTC就是提供端对端连接的实时通信技术,那么既然是端对端,是不是就不需要服务器的参与呢,当然不是。对等端要能够找到对方就需要服务器再对等端之间传递一个sdp信号,甚至还需要额外的turn(作为回退服务器以免端对端连接失败)或者stun服务器(它允许位于NAT(或多重NAT)后的客户 端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。)来做客服NAT。
正是由于webrtc这样的协议特性,很多人误以为使用代理就能完全隐藏我们的真实ip,但是实际并不是这样。事实上,webRTC存在安全风险。推荐使用浏览器插件禁用webRTC(webRTC Control)
浏览器作为一个沙盒环境,没有用户授权,js无法执行高危操作。很多私密操作都没有相应的接口。比如,js不能嗅探dns解析的过程,也不知道本机的网络ip。这也就是为什么网站通常只能获取你的公网ip,无法获取到私有ip。相对应的,你也不可能知道反向代理背后的服务器ip。正是这种对等的私有网络技术,让BS架构和平共处了很久。但是webRTC的出现打破了这个平衡。
webRTC的打洞技术必须要获得对方的ip才能建立连接,当处于内网p2p时,需要获取你的私有ip;当处于互联网p2p时,需要获取你的公网ip(一般是通过stun服务器反射公网ip);正是由于内网p2p提供了获取私有网卡的ip的手段,导致私有地址暴露。
WebRTC 泄漏真实IP原理
攻击者可以通过 JavaScript 或其他技术来访问 WebRTC 中的 API,以获取用户的 IP 地址,从而进行跟踪、监视或攻击。具体来说,攻击者可以编写恶意的script代码插入到网站中,执行跨站点脚本攻击,执行浏览器的webRTC api。除此之外,webRTC的stun/turn服务器也可以泄漏用户的ip地址。因为stun/turn服务器是webRTC穿透和中继的关键组件。
mdns
mdns即是多播dns(去中心化),实现了没有传统dns服务器的情况下使局域网内的主机(局域网内的主机一般都不是静态的,而是DHCP动态分配)能够相互发现和通信,但是其遵循dns协议,使用的端口是5353.
原理:当有主机加入局域网,就会向本机的mdns服务注册一下自己提供什么服务,使用哪个端口。然后向局域网内的所有主机同步一下消息,其他主机也会给出自己的身份响应。当客户端想知道具有给定名称(例如gordon.local)的设备在本地网络中的 IP 地址时,它会向网络发送Multicast DNS并询问哪个网络参与者与主机名匹配。本地网络中的所有设备都会收到请求,然后具有正确名称的设备可以通过多播返回其 IP 地址。适用于主机数量少,想快速组件一个局域网的场景。
mdsn就可以将私有ip替换成域名,这样就可以防止私有ip泄漏。但是其缺点就是损耗性能,每次都需要注册解析。
在chorme中,默认是开启mdns的,所以你获得的就是一段内网的域名从而来避免私有ip泄漏:
下面使用一段代码来获取内网地址:
async function getNetworkIP() {
let found = false;
let resolve;
const promise = new Promise((res) => {
resolve = res;
});
const pc = new RTCPeerConnection({ iceServers: [] });
pc.addEventListener("icecandidate", (e) => {
if (!e.candidate || found) return;
resolve(e.candidate.address);
found = true;
});
pc.createDataChannel("");
pc.createOffer().then((desc) => pc.setLocalDescription(desc));
return promise;
}
await getNetworkIP();
结果就是获得了一个以.local结尾的内网域名
当我们关闭其mdns再来测试chrome://flags/enable-webrtc-hide-local-ips-with-mdns.....
这就是我的内网ip了。。。。。
打洞的原理
打洞其实并不是p2p特有的,传统的cs模型也是打洞,只不过cs模型打洞是单向的,只需要客户端单向打洞。而p2p打洞则是双向的。
假设有两台设备A、B要相互通信。那么就需要一台中介服务器S;
- A向S发起请求,建立连接关系;S收到后保存A所在的公网NAT-A的ip和端口;并且将其发送给B,并且让B给A发送一条消息。
- 这时候A的NAT是没有关于B的映射规则的,所以消息是传递不到A的,没有任何回复;但是这就为A在B的NAT上开了一个端口,后续A的消息进来B是能收到的。
- 之后B再向中介服务器发送一个消息,告诉它我已经准备好了。
- 中介服务器通知A,让A给B发送消息。
- A接收到S的通知开始向B发送消息,因为之前B已经在自己的NAT给A打了一个洞。所以此次A是能成功连接上B的。同时A在连接B的时候,也在自己的NAT上给B打了一个洞。
- 至此,A和B之间的隧道就建立好了,接下来就可以进行点对点的通信了。
注意:连接打通后是有时间限制的,通常需要使用心跳保活。另外,打洞是否成功也要收到NAT的限制,NAT类型(完全锥形、地址受限锥形、端口受限锥形、对称型)不同是会影响到打洞的效果的。