WebRTC в Firefox: почему IPv6-прокси течёт и как это заткнуть
Содержание
- Что вообще происходит при WebRTC-запросе
- Почему IPv6 особенно болезненный
- Как выглядит утечка в цифрах
- Настройки about:config, которые реально работают
- Почему одного about:config мало
- Кейс: Firefox 121 за SOCKS5, v6 утекает
- Кейс: двойной стек и mDNS-маскировка
- Кейс: прокси без UDP, браузер упрямо стучится
- Проверка: что смотреть после правок
- Таблица: что блокирует какой тип утечки
- Что делать, если WebRTC нужен, а утечки нет
- Грабли с IPv6-префиксами и privacy extensions
- Итог по настройкам
Что вообще происходит при WebRTC-запросе
Firefox при инициализации RTCPeerConnection собирает ICE-кандидатов. Это адреса, по которым браузер предлагает установить P2P-соединение. Собираются они с каждого сетевого интерфейса — включая те, что не идут через прокси. Дальше кандидаты уходят в SDP-оффер и попадают к удалённой стороне. Даже если вы сидите за SOCKS5-прокси, браузер честно скажет собеседнику: «я доступен по адресу 2a01:4f8:...».
Механика простая до безобразия. STUN-запрос уходит на сервер (по умолчанию stun.l.google.com:19302), сервер возвращает публичный адрес — тот, с которого пришёл пакет. Если пакет ушёл напрямую, минуя прокси, — вы получаете свой реальный IPv6. Прокси тут ни при чём, он просто не участвует в этом обмене.
Ключевой момент: WebRTC-трафик в Firefox исторически не подчинялся настройкам прокси так, как обычный HTTP. Настройка `network.proxy.socks_remote_dns` решает DNS-утечки, но UDP-трафик ICE идёт отдельным маршрутом. Поэтому SOCKS5-прокси блокирует TCP, а WebRTC спокойно пробивает UDP-дыру.
Почему IPv6 особенно болезненный
IPv4-утечки через WebRTC известны давно, и большинство гайдов сводятся к «отключите WebRTC». С IPv6 ситуация хуже по трём причинам. Первая — двойной стек. У машины одновременно есть IPv4 и IPv6, и ICE собирает кандидатов с обоих. Даже если IPv4 у вас замаскирован, IPv6 может торчать наружу в исходном виде.
Вторая — многие VPN/прокси-клиенты по умолчанию не трогают IPv6. Трафик v6 идёт мимо туннеля, потому что клиент настроен только на v4. Пользователь думает, что защищён, а браузер рапортует глобальный IPv6-адрес провайдера.
Третья — IPv6-адреса часто выглядят «менее подозрительно» для антифрод-систем. Диапазоны вроде `2a01:4f8::/32` (Hetzner) или `2604:a880::/32` (DigitalOcean) прямо указывают на датацентр, но некоторые сервисы фильтруют только IPv4-диапазоны VPS, а v6 пропускают. Это палка о двух концах: и утечка опасна, и прокси-адрес может палиться.
Как выглядит утечка в цифрах
Проверить просто. Открываете `about:config`, ищете `media.peerconnection.enabled`, ставите `false` — и вот у вас уже другой fingerprint. Но давайте по порядку.
Типичный сценарий: браузер за SOCKS5-прокси на `[2a01:4f8:c17:1234::1]:1080`. Открываем https://browserleaks.com/webrtc. Видим публичный IPv4 прокси — хорошо. И тут же видим `2a01:4f8:c17:1234::1` — тоже прокси, тоже хорошо. А теперь другой сценарий: тот же прокси, но клиент настроен только на IPv4-маршрут через туннель. Результат — IPv4 прокси плюс реальный IPv6 вида `2001:db8:a1b2::cafe` от домашнего провайдера. Вот это и есть утечка.
В логах STUN-сервера видно два пакета с разницей в миллисекунды: один пришёл с IP прокси, второй — с реального. Атакующий (или рекламный трекер) просто сопоставляет их по времени и получает связку «прокси ↔ реальный адрес».
Настройки about:config, которые реально работают
Открывайте `about:config`, принимайте риск и правьте. Основная связка:
```
media.peerconnection.enabled = false
media.peerconnection.ice.default_address_only = true
media.peerconnection.ice.no_host = true
media.peerconnection.ice.proxy_only_if_behind_proxy = true
```
Первая настройка убивает WebRTC целиком. Вторая заставляет ICE использовать только дефолтный адрес (без сбора со всех интерфейсов). Третья запрещает host-кандидатов — те самые локальные адреса вроде `fe80::1%eth0`, которые тоже утекают. Четвёртая работает только когда браузер явно за прокси.
Есть ещё `media.peerconnection.ice.obfuscate_host_addresses` — маскирует host-кандидатов через mDNS (`.local`-имена). Полезно, если WebRTC нужен для работы (видеозвонки), но утечки недопустимы.
Проверка после правок:
```bash
curl -6 --socks5-hostname 2a01:4f8:c17:1234::1:1080 https://ifconfig.co
```
Если вернёт IPv6 прокси — маршрут v6 идёт через SOCKS. Если ошибка или пусто — прокси не поддерживает v6, и WebRTC найдёт обходной путь. Проверяйте именно `--socks5-hostname`, а не `--socks5`, иначе DNS резолвится локально.
Почему одного about:config мало
Правки в браузере не отменяют того факта, что операционная система маршрутизирует IPv6 в обход туннеля. Firefox может быть настроен идеально, но если вы запускаете второй браузер, Electron-приложение или обновляете что-то через `apt` — трафик v6 уходит напрямую.
Правильный подход — два уровня. На уровне ОС отключить IPv6 полностью или завернуть его в туннель. На уровне браузера — задать `network.proxy.socks_version = 5` и `network.proxy.socks_remote_dns = true`. Плюс проверить, что в `network.proxy.no_proxies_on` нет `::1` и локальных v6-подсетей, иначе браузер будет ходить мимо прокси на localhost-сервисы.
Для продакшена, где нужен стабильный IPv6-выход без утечек, обычно берут пул адресов и заверяют маршрутизацию на уровне ядра. Например, сервис вроде lexic.ml выдаёт IPv6-прокси с 2015 года — там маршрут уже настроен так, что весь v6-трафик клиента идёт через пул, без «дырявых» интерфейсов. Это снимает головную боль с about:config, но настройки браузера всё равно надо проверить.
Кейс: Firefox 121 за SOCKS5, v6 утекает
Проблема: пользователь настроил SOCKS5-прокси в Firefox 121, проверил на browserleaks — IPv4 чистый, IPv6 показывает домашний адрес `2001:470:1f0a::42`. Прокси-сервер работает, TCP идёт через него, но UDP-пакеты ICE уходят напрямую.
Причина: SOCKS5 в Firefox по умолчанию передаёт только TCP. UDP ASSOCIATE поддерживается не всеми прокси-серверами, и Firefox не пытается его использовать, если сервер не заявил поддержку. ICE-кандидаты собираются с реального интерфейса и уходят в SDP.
Технические детали: в `about:webrtc` видно два ICE-кандидата — `typ host` с адресом `2001:470:1f0a::42` и `typ srflx` с адресом прокси. STUN-запросы к `stun.l.google.com:19302` уходят через UDP-сокет, привязанный к физическому интерфейсу. SOCKS5-обёртка их не перехватывает.
Решение: `media.peerconnection.enabled = false` плюс `media.peerconnection.ice.no_host = true`. После перезапуска `about:webrtc` показывает пустой список кандидатов, browserleaks — только адрес прокси. Если WebRTC нужен — `media.peerconnection.ice.proxy_only_if_behind_proxy = true` и проверка, что прокси поддерживает UDP ASSOCIATE (большинство коммерческих SOCKS5 — нет).
Кейс: двойной стек и mDNS-маскировка
Проблема: корпоративный ноутбук, Firefox 115 ESR, IPv4 и IPv6 одновременно. WebRTC нужен для внутренних видеозвонков, отключать нельзя. После подключения к SOCKS5 в логах STUN видно утечку v6.
Причина: `media.peerconnection.ice.obfuscate_host_addresses = true` маскирует host-кандидатов через mDNS, но srflx-кандидаты (публичные, полученные от STUN) всё равно содержат реальный адрес. mDNS работает только для локальных адресов, публичный IPv6 от провайдера он не спрячет.
Технические детали: в `about:webrtc` видно `typ srflx` с адресом `2a02:2f0a:...` — это публичный v6 от провайдера, полученный напрямую от STUN-сервера. Прокси не участвует. Firefox 115 ESR не поддерживает `ice.proxy_only_if_behind_proxy` в полном объёме — настройка есть, но работает только для host-кандидатов.
Решение: обновиться до Firefox 121+, где `ice.proxy_only_if_behind_proxy` фильтрует и srflx. Плюс на уровне ОС отключить IPv6 на физическом интерфейсе (`sysctl net.ipv6.conf.eth0.disable_ipv6=1`) и оставить только loopback v6. После этого WebRTC работает через v4-прокси, утечек нет, звонки не ломаются.
Кейс: прокси без UDP, браузер упрямо стучится
Проблема: SOCKS5-прокси на базе `dante` не поддерживает UDP ASSOCIATE. Firefox 120 за ним. WebRTC-звонки в Google Meet не работают, но при этом в `about:webrtc` видны кандидаты с реального адреса.
Причина: Firefox пытается собрать srflx-кандидатов через STUN, получает таймаут по UDP через SOCKS (сервер отвечает `0x07 Command not supported`), и переключается на сбор host-кандидатов напрямую. Это документированное поведение ICE: если relay недоступен, используется прямой путь.
Технические детали: в логах dante `sockd` видно `udp.associate: not supported`. Firefox в `about:webrtc` показывает `ICE failed` для srflx, но host-кандидаты с реального IPv6 присутствуют. В SDP-оффере они уходят собеседнику.
Решение: `media.peerconnection.ice.no_host = true` и `media.peerconnection.ice.default_address_only = true`. Meet заработает через TURN-сервер Google (он есть по умолчанию), а host-кандидаты исчезнут. Если TURN тоже недоступен — только отключать WebRTC целиком и использовать отдельный браузер для звонков.
Проверка: что смотреть после правок
Первый чекпоинт — `about:webrtc`. Открываете, смотрите список ICE-кандидатов. Если там есть адреса, не совпадающие с прокси, — правки не применились или нужен рестарт. Второй — https://browserleaks.com/webrtc, смотрите колонку «Public IP». Третий — `about:networking#dns`, проверяете, что DNS-запросы идут через прокси.
Для автоматизации проверки можно гонять headless Firefox с playwright:
```python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.firefox.launch(
firefox_user_prefs={
"media.peerconnection.enabled": False,
"network.proxy.type": 1,
"network.proxy.socks": "2a01:4f8:c17:1234::1",
"network.proxy.socks_port": 1080,
"network.proxy.socks_version": 5,
"network.proxy.socks_remote_dns": True,
}
)
page = browser.new_page()
page.goto("https://ifconfig.co/json")
print(page.inner_text("body"))
browser.close()
```
Скрипт покажет, какой IP видит внешний сервис. Если в JSON `ip` совпадает с адресом прокси — маршрут работает. Если нет — где-то дыра.
Таблица: что блокирует какой тип утечки
| Настройка | host-кандидаты | srflx (STUN) | relay (TURN) |
|---|---|---|---|
| `peerconnection.enabled=false` | блокирует | блокирует | блокирует |
| `ice.no_host=true` | блокирует | пропускает | пропускает |
| `ice.default_address_only=true` | частично | пропускает | пропускает |
| `ice.proxy_only_if_behind_proxy=true` | блокирует | блокирует (FF 121+) | пропускает |
| `ice.obfuscate_host_addresses=true` | маскирует mDNS | не трогает | не трогает |
Комбинация `no_host + default_address_only + proxy_only_if_behind_proxy` закрывает всё, кроме relay. Relay-кандидаты идут через TURN-сервер, который вы сами указали, — это контролируемый канал.
Что делать, если WebRTC нужен, а утечки нет
Сценарий: видеоконференции, P2P-передача файлов, торренты в браузере. Отключать WebRTC нельзя. Тогда настраиваем TURN-сервер, который будет единственным путём для медиа. В `about:config`:
```
media.peerconnection.ice.relay_only = true
```
Эта настройка заставляет ICE использовать только relay-кандидатов. Все медиа-пакеты идут через TURN, реальный адрес не светится. Минус — задержка. TURN добавляет 20–80 мс в зависимости от расстояния до сервера. Для видеозвонков терпимо, для игр — нет.
TURN-сервер поднимается на том же хосте, что и прокси, или отдельно. Конфиг coturn:
```
listening-port=3478
tls-listening-port=5349
fingerprint
lt-cred-mech
realm=turn.example.com
user=user:pass
external-ip=2a01:4f8:c17:1234::1
```
`external-ip` указывает на IPv6-адрес, который увидят клиенты. Если сервер за NAT — указывайте публичный адрес, иначе ICE не соберёт relay-кандидатов.
Грабли с IPv6-префиксами и privacy extensions
Linux по умолчанию использует privacy extensions (`net.ipv6.conf.all.use_tempaddr=2`). Это значит, что IPv6-адрес меняется каждые несколько часов. Для прокси это катастрофа: маршрут настроен на один адрес, а интерфейс уже получил другой. Firefox собирает кандидатов с текущего адреса, и если он не совпадает с тем, что прописан в прокси, — утечка или полный отказ.
Проверяйте `ip -6 addr show`. Если видите `temporary` рядом с адресом — отключайте: `sysctl -w net.ipv6.conf.all.use_tempaddr=0`. Для прокси-хостов это обязательно. Для клиентских машин — по ситуации: privacy extensions защищают от трекинга, но ломают стабильность соединений.
Вторые грабли — `accept_ra`. Если роутер раздаёт IPv6 через RA, а вы думаете, что v6 отключён, — сюрприз. Проверяйте `sysctl net.ipv6.conf.all.accept_ra`. На прокси-клиентах ставьте `0`, если не хотите случайных адресов.
Итог по настройкам
Минимальный набор для параноика: `media.peerconnection.enabled=false`. Для тех, кому WebRTC нужен: `ice.no_host=true`, `ice.default_address_only=true`, `ice.proxy_only_if_behind_proxy=true`, `ice.relay_only=true` плюс свой TURN. Для корпоративных сетей с двойным стеком: отключить IPv6 на физическом интерфейсе, оставить loopback.
Проверяйте после каждого обновления Firefox. Настройки `about:config` иногда сбрасываются при мажорных апдейтах, а новые версии добавляют свои `ice.*`-ключи. `about:webrtc` — ваш главный инструмент диагностики. Если там чисто, значит и снаружи чисто.