Почему это вообще происходит
WebRTC существует, чтобы браузеры могли общаться напрямую — видеозвонки, голос, данные между узлами — не гоняя всё через сервер. Прямые соединения и есть весь смысл, а чтобы их установить, браузеру нужно выяснить, по каким адресам до него можно достучаться.
Он делает это, спрашивая STUN-сервер. Браузер отправляет UDP-пакет на публичный STUN-эндпоинт, а сервер отвечает тем адресом источника, который увидел. Это ваш публичный IP, отражённый обратно и переданный JavaScript в виде ICE-кандидата.
Ключевая деталь: всё это идёт по UDP, вне HTTP-маршрута. Прокси HTTP или SOCKS, настроенный в браузере, обслуживает HTTP-трафик. Пакет STUN им не пользуется. Ваши страницы грузятся через прокси, а обнаружение WebRTC — нет, и нигде ни о какой проблеме не сообщается.
Именно это отличает такой сбой от прочих. Сломанный прокси даёт ошибки. Утечка WebRTC даёт тишину.
Что на самом деле видит сайт
Вся атака достаточно коротка, чтобы прочесть её разом:
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
pc.onicecandidate = e => { if (e.candidate) console.log(e.candidate.candidate); };
pc.createDataChannel('');
pc.createOffer().then(o => pc.setLocalDescription(o));
В каждой строке кандидата есть адрес и тип. Важны три типа:
| Type | Что содержит | Риск |
|---|---|---|
host | Ваш адрес в локальной сети, например 192.168.1.42 | Выдаёт устройство сети; mDNS это смягчает |
srflx | Ваш публичный IP так, как его увидел STUN | Вот это и есть та самая утечка |
relay | Адрес сервера TURN | Ничего о вас не выдаёт |
Если адрес srflx отличается от адреса, с которого приходят ваши HTTP-запросы, вы подарили сайту противоречие, ради которого ему даже не пришлось трудиться: страница считает, что вы во Франкфурте, а WebRTC говорит, что в Милане.
RTCPeerConnection. Строку host маскирует mDNS, строка relay ничего не выдаёт. Ваш публичный адрес несёт только srflx.mDNS решает меньшую половину
Chrome поставляет mDNS-обфускацию локальных адресов начиная с версии 76. Вместо 192.168.1.42 host-кандидаты выглядят как случайный UUID с окончанием .local. Это действительно хорошо и включено по умолчанию.
И это часто понимают неправильно. mDNS касается только кандидатов host — вашего адреса в локальной сети. На кандидатов srflx, которые и несут ваш публичный IP, он не влияет никак. Руководства, советующие включить chrome://flags/#enable-webrtc-hide-local-ips-with-mdns и считать вопрос закрытым, описывают решение для менее важной половины.
Здесь есть и сигнал второго порядка. Обфусцированное имя хоста .local стабильно в пределах сессии, а число host-кандидатов отражает, сколько у машины сетевых интерфейсов. Профиль, сообщающий о шести интерфейсах, выглядит как машина с несколькими VPN-адаптерами — не так, как обычный ноутбук.
Четыре способа с этим справиться
Антидетект-браузеры обычно предлагают тот или иной вариант этих четырёх. Они не равноценны, и лучший зависит от того, чем вы заняты.
Полностью отключено
Ни RTCPeerConnection, ни кандидатов, ни утечки. Чисто, но заметно: WebRTC уже десять лет является стандартом в любом браузере, и Chrome без него — редкость. Подойдёт для аккаунтов, которые никогда не касаются звонков и стримов. Не подойдёт для всего, что их использует: некоторые сайты уже применяют наличие WebRTC как проверку «живости».
UDP отключён
API существует и отвечает, но UDP наружу не идёт, поэтому кандидат srflx не появляется. Менее заметно, чем полное удаление, и утечку это действительно закрывает. Оно же ломает реальную работу WebRTC, что может быть важно, а может и нет.
Реальный
Пропускать всё как есть. Это правильно ровно в одном случае: когда ваш прокси обрабатывает и UDP. Прокси SOCKS5 с UDP-ассоциацией или системный VPN проводит STUN-пакет через тот же выход, что и HTTP-трафик, и отражённый адрес оказывается адресом прокси. В такой схеме реальный WebRTC — самый достоверный из доступных вариантов. Проверьте это, а не считайте само собой разумеющимся.
Изменённый
Заменяйте обнаруженный публичный адрес на адрес выхода прокси до того, как он попадёт в JavaScript. WebRTC продолжает работать, кандидаты присутствуют в обычном количестве и согласуются с HTTP-маршрутом. Это самая убедительная конфигурация, когда она сделана на уровне движка: подстановка должна происходить там, где кандидаты порождаются, а не в контент-скрипте, иначе они разойдутся и вы вернётесь к началу.
Как проверять правильно
Большинство «тестов на утечку WebRTC» сообщают лишь, появился ли публичный адрес. Вопрос не в этом. Вопрос в том, совпадает ли он с вашим прокси.
Надёжная процедура занимает две минуты:
- Запишите адрес выхода, который прокси должен вам дать.
- Откройте
browserleaks.com/webrtcв профиле и посмотрите строку публичного IP. - Сравните. Совпало — хорошо. Не совпало — утечка, даже если чужой адрес не является вашим домашним: адрес дата-центра рядом с резидентным HTTP-маршрутом уже сам по себе противоречие.
- Повторите с отключённым прокси профиля. Если значение не изменилось, WebRTC вообще не идёт через прокси, а прежнее совпадение было везением.
Именно четвёртый шаг чаще всего пропускают, и именно он вскрывает неверную настройку.
Какое место это занимает среди остального
WebRTC заслужил свою репутацию, но соразмерность терять не стоит. Это один сигнал, и один из самых простых для окончательного закрытия: настроили один раз на профиль, проверили один раз — и он больше не беспокоит.
Чем он не является, так это заменой всему остальному. Профиль с идеальной обработкой WebRTC и несогласованным отпечатком попадётся на несогласованности. Профиль за серверным прокси попадётся на ASN, течёт WebRTC или нет.
Причина чинить его первым не в том, что это самый важный сигнал. А в том, что это единственный, который отказывает молча, а о молчаливых отказах узнают из письма о блокировке.