Утечка DNS через прокси: как проверить и закрыть запросы к службе имён
Утечка запросов к службе имён происходит тогда, когда программа сама превращает домен в адрес через локальный резолвер и только после этого открывает соединение через посредника. Закрывается она переносом разрешения имени на сторону прокси: в SOCKS за это отвечает схема socks5h, в HTTP-прокси домен и так уходит внутри запроса, остальное настраивается по месту.
Дальше разбираем механику по шагам: почему обращение к службе имён живёт отдельно от полезного трафика, чем socks5 отличается от socks5h, как поймать утечку через tcpdump и через журнал своего сервера имён, как перевести на посредника браузеры, curl, Python, Node.js, парсеры и антидетект-браузеры. Отдельно смотрим на системные службы и на DNS поверх HTTPS.
Что такое утечка запросов к службе имён
Любое соединение с сайтом состоит из двух разных операций. Сначала домен превращается в адрес, потом по этому адресу открывается канал. Первая операция идёт по своему протоколу на порт 53, вторая по TCP на порт сайта. Прокси стоит на второй операции, и если первую никто не перенаправил, она уходит с машины напрямую.
Со стороны наблюдателя картина складывается за минуту. Владелец резолвера видит перечень доменов, точное время обращений и адрес машины, которая спрашивает. Целевой сайт при этом видит выходной адрес из пула и считает картину нормальной. Два наблюдателя смотрят на разные половины одного действия, и полнота маскировки теряется на первой половине.
Практических последствий три. Первое: список посещаемых доменов оказывается у стороны, которой его видеть не нужно. Второе: авторитетный сервер зоны фиксирует обращение с адреса, который географически и по подсети никак не связан с выходным адресом прокси. Третье, самое обидное для рабочих задач, это расхождение ответов. Локальный резолвер отдаёт один адрес балансировщика, посредник открыл бы соединение с другим, и площадка отвечает иначе, чем ожидалось.
Мы проверяем эту связку первой, когда пользователь пишет «адрес подменился, а сайт всё равно ведёт себя странно». Выходной адрес в таких случаях показывается верно, серия запросов проходит, коды ответов приличные. Ломается поведение: разные версии страницы, лишние проверки, случайные редиректы. Причина сидит в том, что имя разрешалось локально и соединение пошло к другому узлу площадки.
Отдельно стоит запомнить: утечка запросов к службе имён никак не связана с качеством адресов. Тут работает настройка софта. Один и тот же пакет прокси с одной и той же строкой подключения течёт в одной программе и держится плотно в другой.
Схемы socks5 и socks5h: где именно разрешается имя
Протокол SOCKS умеет принимать в команде подключения два вида цели: адрес и доменное имя. Тип цели передаётся отдельным байтом, и клиент выбирает его сам. Отсюда и родилось разделение схем в строке подключения, которое многие принимают за косметику.
Схема socks5:// говорит библиотеке: разреши имя сам, дальше передай посреднику готовый адрес. Схема socks5h:// говорит обратное: передай посреднику само имя, пусть он спросит свой резолвер. Буква h в конце означает «hostname», и это единственное, что отделяет закрытую схему от текущей. У SOCKS4 та же история: базовая версия имён не принимает совсем, расширение socks4a принимает.
# имя разрешается на машине, наружу уходит запрос к службе имён
curl -x socks5://user5521:[email protected]:1080 https://example.com
# имя разрешает посредник, с машины наружу не уходит ничего лишнего
curl -x socks5h://user5521:[email protected]:1080 https://example.com
| Схема подключения | Кто превращает имя в адрес | Что уходит с машины наружу | Течёт |
|---|---|---|---|
socks4:// | локальный резолвер | запрос на порт 53 плюс TCP через посредника | да |
socks4a:// | сервер прокси | только TCP через посредника | нет |
socks5:// | локальный резолвер | запрос на порт 53 плюс TCP через посредника | да |
socks5h:// | сервер прокси | только TCP через посредника | нет |
http:// к обычной странице | сервер прокси | домен в первой строке запроса | нет |
http:// к странице с шифрованием | сервер прокси | домен в заголовке CONNECT | нет |
| системная служба мимо настроек | локальный резолвер | запрос на порт 53 | да |
Важная деталь про поддержку схем. Слово socks5h понимают curl, библиотека requests в Python и почти все обёртки поверх неё, а вот интерфейсы многих программ дают одно поле «SOCKS5» и переключатель рядом. Переключатель называется по-разному: «resolve hostnames remotely», «проксировать DNS», «remote DNS». Смысл у него ровно один, и он включается один раз на профиль. Такую передачу имён поддерживает пакет с протоколом SOCKS5 для программ: адреса те же самые, меняется только схема в строке.
Ещё одна тонкость касается UDP. Обращение к службе имён по умолчанию идёт по UDP, и SOCKS5 умеет пропускать UDP через команду ассоциации. Это отдельная механика, разобранная в материале про TCP и UDP через SOCKS5. Для закрытия утечки она не требуется: при socks5h резолвит сам сервер прокси, и UDP с машины никуда не идёт.
HTTP-прокси и туннель CONNECT: имя уходит само
С HTTP-прокси ситуация проще, и это тот случай, когда протокол работает в нашу пользу по умолчанию. Клиент отправляет посреднику запрос с полным адресом ресурса в первой строке. Домен лежит прямо там, в тексте запроса, и разрешать его на машине незачем.
GET http://example.com/catalog/page-2 HTTP/1.1
Host: example.com
Proxy-Authorization: Basic dXNlcjU1MjE6cGYzOWtk
Для страниц с шифрованием работает туннель. Клиент просит посредника открыть канал командой CONNECT, и в этой команде тоже стоит домен с портом:
CONNECT example.com:443 HTTP/1.1
Host: example.com:443
Proxy-Authorization: Basic dXNlcjU1MjE6cGYzOWtk
Посредник принимает имя, разрешает его у себя, открывает TCP и дальше просто гоняет байты в обе стороны. Разбор самой механики туннеля собран в отдельном материале, а нам здесь важен один вывод: при работе через прокси HTTP и HTTPS для браузеров запросы к службе имён с машины не уходят, если только браузер не резолвит имена заранее по своим причинам.
Причины такие есть. Браузеры делают упреждающее разрешение имён для ссылок на странице, чтобы ускорить переходы. Эта функция живёт отдельно от настроек прокси и включена по умолчанию. Ещё один источник это расширения: они ходят на свои эндпоинты своими средствами. Поэтому даже с HTTP-прокси браузер проверяется отдельно. Считать его закрытым по одному факту протокола рано.
Проверка своими руками: два надёжных способа
Спорить о схемах бессмысленно, когда есть возможность посмотреть на трафик. Два способа дают однозначный ответ за пару минут.
tcpdump на порту 53
Ставим захват на интерфейс, фильтруем по порту службы имён, запускаем проверяемый запрос из соседнего окна и смотрим, появились ли пакеты.
# слушаем обращения к службе имён на всех интерфейсах
sudo tcpdump -n -i any port 53
# в другом окне: заведомо текущая схема
curl -s -x socks5://185.24.87.14:1080 https://example.com -o /dev/null
# и закрытая схема, вывод tcpdump должен остаться пустым
curl -s -x socks5h://185.24.87.14:1080 https://example.com -o /dev/null
При текущей схеме в захвате появится строка вида IP 10.0.0.5.51234 > 1.1.1.1.53: 42817+ A? example.com. (29). Тут видно всё: адрес машины, адрес резолвера, тип записи и сам домен. При схеме socks5h окно захвата остаётся пустым, и это самый убедительный вид проверки.
На Windows тот же приём делается через Wireshark с фильтром отображения dns. Логика одинаковая: запускаем захват, дёргаем один запрос, смотрим на список пакетов. Пустой список означает, что имя разрешил посредник.
Журнал своего сервера имён
Второй способ подходит там, где до консоли рабочей машины не дотянуться. Мы поднимаем свой авторитетный сервер зоны, направляем на него отдельный поддомен и смотрим журнал обращений. Каждый запрос к уникальному имени оставляет строку с адресом того, кто спрашивал.
# на своём сервере включаем журнал запросов
rndc querylog on
tail -f /var/log/named/queries.log
# с рабочей машины дёргаем уникальное имя через проверяемый софт
curl -s -x socks5h://185.24.87.14:1080 http://t7k3q.probe.example.net/ -o /dev/null
В журнале появится строка с адресом резолвера, который пришёл за записью. При закрытой схеме это будет резолвер провайдера, где стоит сервер прокси, и подсеть совпадёт с подсетью выходного адреса. При утечке в журнале окажется резолвер домашнего или офисного канала. Способ хорош тем, что показывает конкретный адрес, который выдал утечку, вместе с точным временем обращения.
| Шаг проверки | Инструмент | Признак утечки | Признак закрытой схемы |
|---|---|---|---|
| 1. Захват трафика | tcpdump -n port 53 | пакеты с доменом в теле запроса | пустой вывод во время запроса |
| 2. Уникальное имя | свой сервер зоны и журнал | в журнале адрес локального канала | в журнале адрес со стороны прокси |
| 3. Выходной адрес | curl https://ifconfig.me | адрес машины | адрес из пула |
| 4. Браузерная проверка | страница проверки утечки | резолвер провайдера в списке | резолвер со стороны посредника |
| 5. Повтор под нагрузкой | прогон на 200 запросов | часть обращений уходит мимо | вывод захвата остаётся пустым |
Пятый шаг пропускают чаще всего, и напрасно. Некоторые библиотеки держат свой кэш имён и при разогретом кэше ничего наружу не отправляют, поэтому единичная проверка проходит успешно. Утечка вылезает через полчаса прогона, когда записи в кэше протухли. Мы всегда повторяем проверку на живом прогоне. Одиночного запроса для вывода мало.
Браузеры: как перевести разрешение имён на посредника
Firefox настраивается через about:config и делает это честнее прочих. Нужны три параметра.
network.proxy.socks_remote_dns = true
network.dns.disablePrefetch = true
network.dns.disablePrefetchFromHTTPS = true
Первый переводит разрешение имён на сторону SOCKS. Второй и третий выключают упреждающее разрешение ссылок со страницы, которое идёт мимо настроек прокси. После правки браузер перезапускается, и проверка делается заново через захват трафика.
Браузеры на Chromium в настройке SOCKS передают имена посреднику самостоятельно, когда прокси задан ключом запуска. Проблема сидит в упреждающем разрешении и в служебных обращениях самого браузера. Отключается это ключами и настройкой политики:
chrome.exe --proxy-server="socks5://185.24.87.14:1080" ^
--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE 185.24.87.14" ^
--disable-features=AsyncDns
Правило host-resolver-rules заворачивает любое имя в отказ на уровне локального резолвера и оставляет живым только адрес самого прокси. Если что-то попробует разрешить имя мимо посредника, оно просто не получит ответа. Приём жёсткий, зато проверяемый: после него в захвате трафика тишина.
Для постоянной работы это же задаётся политикой браузера. На Windows ветка HKLM\SOFTWARE\Policies\Google\Chrome, параметры ProxyMode со значением fixed_servers, ProxySettings с описанием сервера и BuiltInDnsClientEnabled со значением ноль. Политика переживает обновления браузера и не сбрасывается пользователем.
curl, Python и Node.js: рабочие строки подключения
В curl всё решается схемой, разбор которой был выше. Для проверки полезен ключ -v: он показывает, что именно уходит посреднику.
curl -v -x socks5h://user5521:[email protected]:1080 https://example.com 2>&1 | grep -i socks
В Python библиотека requests работает через PySocks. Установка pip install requests[socks], дальше словарь прокси со схемой socks5h:
import requests
proxies = {
"http": "socks5h://user5521:[email protected]:1080",
"https": "socks5h://user5521:[email protected]:1080",
}
r = requests.get("https://example.com", proxies=proxies, timeout=20)
print(r.status_code, len(r.content))
Одна буква в обеих строках, и разрешение имён переезжает на сервер. Тот же приём работает в httpx и в aiohttp через aiohttp_socks. В aiohttp берётся ProxyConnector.from_url с той же схемой socks5h.
В Node.js через axios или undici используется агент socks-proxy-agent, и он передаёт имя посреднику по умолчанию. Проверить это стоит явно, потому что часть кода в проектах написана через SocksClient с ручным разрешением имени:
const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');
const agent = new SocksProxyAgent('socks5h://user5521:[email protected]:1080');
axios.get('https://example.com', { httpAgent: agent, httpsAgent: agent })
.then(res => console.log(res.status, res.headers['content-type']))
.catch(err => console.error(err.message));
Отдельно про пул соединений. Библиотеки переиспользуют TCP между запросами, и при смене выходного адреса это иногда мешает. Когда нужна разная точка выхода на каждый запрос, агент создаётся заново либо ставится заголовок Connection: close. Ротация внутри пула автоматическая, поэтому новое соединение штатно уходит с другого адреса. Такой режим удобен для сбора данных, и под него берут прокси для парсинга и массового сбора.
Парсеры и антидетект-браузеры
A-Parser берёт прокси списком и работает с ним по своей схеме. В настройках прокси-чекера указывается тип socks5, а передача имён включается галочкой в свойствах потока. ZennoPoster работает через свой сетевой слой: там прокси задаётся на уровне инстанса браузера, и имена уходят посреднику, когда выбран режим SOCKS5. Key Collector использует прокси на уровне HTTP-запросов, и там доменное имя всегда лежит в строке запроса.
Антидетект-браузеры закрывают вопрос настройкой профиля. В карточке профиля есть поле прокси и рядом с ним переключатель передачи имён. Смысл конструкции в том, что профиль хранит весь набор параметров сразу: выходной адрес, отпечаток, часовой пояс, язык интерфейса и режим разрешения имён. Мы советуем сверять эти поля между собой при создании профиля, потому что несогласованный набор виден площадке лучше, чем открытый адрес. Профиль с передачей имён на посредника и выходным адресом из пула выглядит ровно, и именно к такому состоянию мы ведём настройку.
Проверка профиля делается так же, как проверка любого софта: захват трафика на порту 53 и одна навигация внутри профиля. Одна навигация, один взгляд на вывод. Если строки появились, переключатель стоит в неверном положении либо расширение внутри профиля ходит своим маршрутом.
Что остаётся вне настроек софта: системные службы и шифрованный DNS
Службы, которые ходят к именам мимо посредника
Настройка софта закрывает софт. Операционная система живёт своей жизнью и обращается к службе имён по своим поводам: проверка обновлений, синхронизация времени, телеметрия, проверка доступности сети, сетевые диски, клиенты обмена сообщениями в автозапуске. Все они резолвят имена локально и не знают ничего про прокси в браузере.
На рабочей станции самый практичный порядок такой. Смотрим, что вообще стучится на порт 53, и разбираемся адресно:
# кто именно обращается к службе имён прямо сейчас
sudo tcpdump -n -i any port 53 -c 20
# на Windows: активные соединения и процессы
netstat -ano -p udp | findstr :53
Дальше есть два рабочих пути. Первый: развернуть рабочую задачу на отдельной виртуальной машине или в контейнере, где системных служб почти нет, и весь исходящий трафик заворачивается на посредника. Второй: поднять локальный перенаправитель, который принимает запросы на порт 53 и уводит их в туннель. Для серверных задач первый путь удобнее, потому что контейнер поднимается за минуту и не тянет за собой лишние процессы.
# контейнер с прокси в переменных окружения
docker run --rm \
-e ALL_PROXY=socks5h://user5521:[email protected]:1080 \
-e HTTPS_PROXY=socks5h://user5521:[email protected]:1080 \
python:3.12-slim python -c "import urllib.request;print(urllib.request.urlopen('https://ifconfig.me').read())"
Переменные ALL_PROXY, HTTP_PROXY и HTTPS_PROXY понимают curl, wget, pip, git, requests и десятки других инструментов. Одна строка в окружении закрывает большую часть консольной работы. Регистр имеет значение на части систем, поэтому мы обычно прописываем оба варианта, строчными и заглавными буквами.
Серверная сторона проще станции. Там мы держим минимум служб, весь исходящий трафик задачи идёт через один канал, и проверка сводится к одному захвату. Постоянные прогоны с серверов удобно вести через приватный доступ к серверным адресам, где картина трафика предсказуема от запуска к запуску.
DNS поверх HTTPS: что меняется и что остаётся
DNS поверх HTTPS прячет содержимое запроса от провайдера, упаковывая его в обычное шифрованное соединение с известным сервисом. Для наблюдателя на канале домены становятся невидимыми, и это полезная механика сама по себе. Только к прокси она отношения не имеет.
Механика такая: браузер с включённым DoH обращается к своему провайдеру имён напрямую с адреса машины. Домен уходит от площадки-наблюдателя, зато провайдер имён получает и домен, и адрес спрашивающего. С точки зрения работы через посредника это та же утечка, только упакованная в шифрование. Хуже того, включённый DoH иногда перебивает настройку socks_remote_dns, потому что браузер идёт к своему сервису раньше, чем обращается к прокси.
Порядок настройки поэтому такой: если разрешение имён отдано посреднику, DoH в браузере выключается. Дублировать одно другим смысла нет, а конфликт двух механизмов даёт непредсказуемое поведение.
# Firefox, about:config
network.trr.mode = 5
# Chromium через политику: параметр DnsOverHttpsMode
DnsOverHttpsMode = "off"
Значение 5 в Firefox означает полное выключение DoH пользователем. Значения 2 и 3 включают режимы с разной степенью настойчивости, и оба они уводят запросы мимо SOCKS. После правки проверяем захватом: обращения на порт 53 отсутствуют, шифрованных обращений к сервису DoH тоже нет.
Отдельный случай это операционная система с собственным шифрованным резолвером. Такой резолвер работает вне браузера и обслуживает все программы разом. Он выключается в сетевых настройках системы, и проверяется тем же захватом трафика по адресу известного сервиса имён.
Что остаётся проверить после настройки
Настройка считается законченной, когда три проверки подряд дают одинаковый результат. Захват трафика на порту 53 молчит во время запроса. Журнал своего сервера зоны показывает резолвер со стороны прокси. Выходной адрес совпадает с адресом из пула. Три совпадения означают, что имя и соединение идут одним маршрутом.
Повторять проверку стоит после каждого обновления браузера и после каждой смены версии библиотеки. Обновления сбрасывают часть настроек к значениям по умолчанию, и упреждающее разрешение имён возвращается тихо. Проверка занимает две минуты, поэтому мы ставим её в тот же список, где лежит проверка выходного адреса.
Удобнее всего провести всю серию на бесплатном тесте до 2 часов. Пакет включается примерно за 5 минут, список адресов приходит в форматах IP:PORT и IP:PORT:LOGIN:PASS, и оставшегося времени хватает на настройку софта, три проверки и короткий прогон под нагрузкой. Свои команды и свои цифры отвечают на вопрос точнее любого описания, и пакет прокси IPv4 с доступом к пулу проверяется именно так.
Частые вопросы
Какой тип прокси выбрать, чтобы имена уходили посреднику?
В пакете IPv4 доступны SOCKS4 и SOCKS5 на выбор, рекомендуется SOCKS5. Базовый SOCKS4 доменные имена не принимает совсем, поэтому передача имён на сторону сервера там работает только через расширение. SOCKS5 принимает имя штатно, и в строке подключения для этого достаточно схемы socks5h. Строку удобно собирать сразу под нужный софт, взяв адреса из списка SOCKS5 для скриптов и парсеров.
Подойдут ли прокси под мою задачу, если софт нестандартный?
Заранее предугадать поведение каждой программы нельзя, поэтому перед покупкой доступен бесплатный тест до 2 часов под ваш запрос. Порядок такой: выбрать тип прокси под свой софт, зарегистрироваться в кабинете, отправить запрос теста из меню, указать свой адрес в настройках, активировать доступ, затем написать оператору логин и тип прокси. Проверка передачи имён как раз входит в те действия, которые успеваются за тестовое окно.
Почему выходной адрес каждый раз другой, это влияет на разрешение имён?
Пул держится в районе 12 000 активных адресов, ротация идёт автоматически внутри пула, список обновляется в реальном времени. Смена выходного адреса между запросами это штатная работа. На разрешение имён она не влияет: при схеме socks5h имя разрешает тот сервер, через который идёт конкретное соединение, и обращение к службе имён с вашей машины не уходит в любом случае.
Чем проверить пачку адресов, если их несколько сотен?
Для массовой проверки подходит чекер от Zennolab, у него есть демонстрационная версия: он проходит список пачкой и показывает отвечающие строки, время отклика и тип прокси. Проверку передачи имён он не заменяет, поэтому её мы делаем захватом трафика на одном произвольном адресе из списка. Поведение схемы одинаково для всего пула, поэтому одной пробы достаточно.
Дальше по диагностике собраны соседние разборы: базовая проверка доступа командами и браузером лежит в материале про то, как проверить работу прокси, разбор заголовков и того, что уходит на сторону площадки, собран в проверке анонимности, а второй канал раскрытия адреса разобран в заметке про проверку и отключение WebRTC. Если настройки нужно закрепить на уровне рабочей машины и серверных задач, порядок описан в материале про подключение в программах и на сервере.