DEV Community

全网低价IP
全网低价IP

Posted on Originally published at socks5ip.com.cn

WebRTC Leaks Your Real Address Even Behind a Proxy. Here Is the Path It Takes.

You pointed the browser at a SOCKS5 exit, loaded an IP echo page, and it returned the country you paid for. Then a call widget, a support chat, or a conferencing SDK negotiated a session, and the other side logged an address from your home ISP. Nothing was misconfigured in the tunnel. A different subsystem opened its own route out.

What the browser is actually collecting

WebRTC needs a peer-to-peer path, so before any media flows it runs ICE: it enumerates every way another host could reach this machine and hands that list to the remote party. Three candidate types matter here.

Host candidates are the machine's own interface addresses. Modern browsers obfuscate them as random .local mDNS names, so a stranger no longer reads 192.168.x.x directly — but the obfuscation is cosmetic, not a privacy boundary.

Server-reflexive candidates are the ones that hurt. The browser sends a STUN binding request to a public server and learns the address that server saw. That is your real public IP, measured on the machine's own network path.

Relay candidates only exist when a TURN server was configured, which is the single case where the address belongs to infrastructure instead of you.

The mechanism is structural: STUN is a UDP conversation with a third party, started by the browser, independent of whatever TCP tunnel you aimed at the page you were reading.

Four surfaces, one of which is not covered

Surface What leaves Transport Covered by a SOCKS5 proxy?
Host candidate Local interface address mDNS, link-local No — never enters the tunnel
srflx via STUN Public IP of your real path UDP to 3478 Only with UDP ASSOCIATE
DNS resolution Resolver address, query names UDP/TCP 53 Client-dependent
The HTTP request The page you asked for TCP Yes, when scoped correctly

Notice that three of the four rows are UDP or link-local, while the proxy you configured is usually a TCP-only CONNECT. The page loads through the tunnel; the media negotiation does not.

Why the tunnel does not catch it

RFC 1928 defines exactly three commands. Most client libraries implement only the first and then advertise themselves as "SOCKS5 support".

CMD Name What it actually carries
0x01 CONNECT One outbound TCP stream
0x02 BIND Inbound TCP, rarely implemented
0x03 UDP ASSOCIATE Datagrams through a relay port

A CONNECT-only client has no vocabulary for a datagram. It cannot forward the STUN request, and it usually does not fail loudly — the application simply falls back to the local path that is still available. That fallback is the leak.

This is the same root cause that makes some game clients behave oddly behind a proxy, and it is why "the proxy works" and "UDP works" are two separate facts you have to verify separately.

Prove it in twenty lines

Before touching browser flags, ask the exit whether it will carry datagrams at all. The greeting is two bytes, and the reply code tells you everything.

import socket

def probe_udp_associate(host, port, timeout=8):
    s = socket.create_connection((host, port), timeout)
    s.sendall(b"\x05\x01\x00")                  # ver 5, 1 method, no-auth
    ver, method = s.recv(2)
    if (ver, method) != (5, 0):
        return f"handshake refused: method={method:#04x}"
    s.sendall(b"\x05\x03\x00\x01" + b"\x00" * 6)  # UDP ASSOCIATE, 0.0.0.0:0
    ver, rep, _, atyp = s.recv(4)
    if rep != 0x00:
        return f"REP={rep:#04x} " + ("cmd unsupported" if rep == 0x07 else "refused")
    size = {0x01: 4, 0x04: 16}.get(atyp) or s.recv(1)[0]
    s.recv(size + 2)                             # relay address + port
    return "UDP ASSOCIATE accepted"
Enter fullscreen mode Exit fullscreen mode

REP=0x07 means command not supported, and it is the answer you should expect from a plain TCP proxy. Note that acceptance is necessary but not sufficient: the browser still has to be told to use that path.

Closing it, layer by layer

Layer Action What it changes
Browser policy Chromium WebRtcIPHandlingPolicy=disable_non_proxied_udp Forbids UDP that bypasses the proxy
Browser policy Firefox media.peerconnection.ice.proxy_only=true Same intent, different key
Stack Disable IPv6 on the egress interface Removes a whole parallel address space
Profile Use a fingerprint browser that scopes UDP per profile Policy ships pre-set, survives restarts

The Chromium value is the important one to read carefully: it does not force UDP through the proxy, it forbids UDP that would not go through it. Calls may fail as a result. For isolation work that is the correct trade — a broken call is cheaper than a logged home address.

Five mistakes that keep the leak alive

  1. Confirming only the HTTP path. An IP echo page proves CONNECT works; it says nothing about ICE.
  2. Assuming a proxy extension covers WebRTC. Extensions that rewrite proxy settings do not touch the ICE policy, which lives elsewhere.
  3. Treating mDNS .local candidates as safe. They hide the LAN address, not the public one seen by a STUN server.
  4. Blocking UDP 3478 only. Several clients retry the same STUN service over TCP/443, which your tunnel may well forward.
  5. Re-testing after an exit change with the old browser profile. A new IP with a stale policy state reads as a regression that is not one.

FAQ

Does UDP ASSOCIATE alone stop the leak?
No. It makes datagrams reachable through the exit, which is a prerequisite. The client must also be configured to use that path, otherwise it keeps choosing the direct one.

Do desktop apps and mobile SDKs leak the same way?
Yes, often more quietly. Conferencing and game SDKs frequently bundle their own STUN logic, and the operating-system proxy setting never reaches it.

Is a relay candidate a good sign?
It is the one candidate type that does not expose your path, because the address belongs to a TURN server. The cost is bandwidth and latency, since every packet takes a detour.

Does this matter for scraping and API clients?
Rarely. A pure HTTP client cannot gather ICE candidates. The risk appears when you render pages in a real browser profile, which is exactly what anti-detect setups do.

Where this sits in an egress stack

Candidate policy is a property of the client, not of the exit, so a well-reputed IP and a correct tunnel still leave this path open until the browser is configured. The WebRTC leak walkthrough on socks5ip.com.cn covers the per-browser detection and fix steps, and the IP check centre confirms what the exit looks like from outside before you go chasing client behaviour.

Audit one layer at a time, and write down which layer you changed. Most "the proxy leaks" reports turn out to be an unmodified browser policy, not a dishonest exit.

Top comments (0)