- Почему IPv6-прокси «фонит» через PTR-запись: как обратная зона DNS раскрывает пул
Содержание
- Что такое PTR и почему он вообще существует
- Как PTR выдаёт пул
- Почему IPv6 фонит сильнее IPv4
- Как это ловят на практике
- !/bin/bash
- Что видит сайт: заголовки и цепочка
- Кейс: парсинг маркетплейса через IPv6-пул
- Кейс: утечка через SNI и PTR вместе
- Кейс: PTR отсутствует — и это палит
- Как проверить свой пул на «фон»
- Настройка PTR на своей зоне
- Что делать, если PTR у чужого пула
Что такое PTR и почему он вообще существует
PTR-запись — обратное преобразование IP-адреса в доменное имя. Работает через специальную зону `in-addr.arpa` для IPv4 и `ip6.arpa` для IPv6. Запрос идёт по адресу, записанному в обратном порядке с точками между нибблами (4-битными группами).
Для IPv4 всё просто: `192.0.2.45` → `45.2.0.192.in-addr.arpa`. Для IPv6 — зверски длинно. Адрес `2001:db8:abcd::1` разворачивается в 32 ниббла: `1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.8.b.d.0.1.0.0.2.ip6.arpa`. Именно эту строку резолвер шлёт в DNS.
Зачем это нужно? Исторически — для диагностики. `traceroute`, логи почтовых серверов, антиспам-фильтры. Сейчас PTR — это в первую очередь сигнал репутации: если у IP нет обратной записи или она указывает на `dynamic-pool-42.isp.example`, любой сервис делает стойку.
Как PTR выдаёт пул
Провайдеры прокси редко заморачиваются с PTR для каждого адреса. Настройка обратной зоны — это делегирование от RIR, потом зона у владельца блока, потом скрипт, который генерирует тысячи записей. Дешевле поставить одну шаблонную запись на весь /64 или /48.
Отсюда типичная картина: клиент приходит на сайт с адресом `2a01:4f8:c17:1a2f::5`, а PTR возвращает `customer-2a01-4f8-c17-1a2f.vps.hosting.example`. Всё, пул раскрыт. Хостер известен, диапазон известен, паттерн известен.
Второй сценарий — PTR вообще нет. Резолвер получает NXDOMAIN или SERVFAIL. Для антифрода это тоже сигнал: живой eyeball-адрес почти всегда имеет PTR от провайдера (Ростелеком, МТС, Comcast, Deutsche Telekom). Отсутствие PTR у IPv6-адреса — нетипично для конечного пользователя, зато типично для датацентра и прокси-фермы.
Почему IPv6 фонит сильнее IPv4
С IPv4 всё устоялось: пулы огромные, адреса переиспользуются годами, PTR часто рандомизирован или отсутствует у мелких провайдеров. IPv6-блоки выдаются крупными префиксами (/32, /48, /64), и провайдеры поначалу лепили PTR по одной схеме на весь блок.
Пример: сервер на nginx 1.24 с проверкой через `ngx_http_realip_module` и GeoIP2 показал, что из 5000 запросов с IPv6-адресов одного /48-префикса 4600 имели PTR вида `*.static.provider.example`. Один шаблон — весь пул на ладони.
Плюс сказывается длина адреса. IPv6-адрес сам по себе несёт информацию: префикс /32 говорит о провайдере, /48 — о площадке, /64 — о конкретной подсети. Даже без PTR по одному адресу можно прикинуть, откуда ноги растут. PTR добавляет к этому человекочитаемое имя.
Как это ловят на практике
Три уровня проверки, которые встречаются в антифрод-системах и WAF.
Первый — прямой DNS-запрос PTR при заходе. Дорого по latency (20–80 мс), поэтому кэшируют. Второй — офлайн-обогащение логов: раз в сутки прогоняют базу IP через PTR и строят карту «префикс → паттерн». Третий — сравнение PTR с rDNS-именем, которое клиент отдаёт в TLS SNI или HTTP-заголовках (редко, но бывает).
Вот как выглядит ручная проверка пула через `dig`:
```bash
!/bin/bash
for i in \$(seq 1 20); do
addr=\$(printf "2a01:4f8:c17:1a2f::%x" "\$i")
nibbles=\$(echo "\$addr" | sed 's/://g' | rev | sed 's/./&./g')
ptr=\$(dig +short -x "\$addr" @1.1.1.1)
echo "\$addr -> \${ptr:-NXDOMAIN}"
done
```
Прогон по 20 адресам из одной /64 покажет, одинаковая ли схема именования. Если 18 из 20 вернули `*.vps.hosting.example` — пул палится одним взглядом.
Что видит сайт: заголовки и цепочка
Сайт получает IP из TCP-соединения. За обратным прокси — из `X-Forwarded-For` или `Forwarded`. Дальше приложение (или edge-функция) делает PTR-запрос. В логах Cloudflare, к примеру, поле `ClientASN` и `ClientIP` уже есть, а PTR подтягивается отдельным воркером.
Типичная цепочка проверки на бэкенде:
```python
import dns.resolver
import dns.reversename
def enrich(ip: str) -> dict:
rev = dns.reversename.from_address(ip)
try:
answers = dns.resolver.resolve(rev, "PTR", lifetime=1.5)
names = [str(r.target).rstrip(".") for r in answers]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer,
dns.resolver.LifetimeTimeout):
names = []
return {
"ip": ip,
"ptr": names,
"suspicious": not names or any(
kw in n for n in names
for kw in ("vps", "proxy", "static", "dynamic", "pool", "hosting")
),
}
```
Таймаут 1.5 секунды — компромисс. Меньше — теряем медленные резолверы, больше — пользователь ждёт на пустом месте. Результат кэшируют в Redis на 6–24 часа: PTR меняется редко.
Кейс: парсинг маркетплейса через IPv6-пул
Задача — собирать цены с площадки, которая блокирует по IP. Взяли пул /64 у провайдера, 500 адресов. Через сутки — бан 90% адресов.
Причина: у всего /64 стоял PTR `node-{index}.proxyfarm.example`. Антифрод-система площадки резолвила PTR при первом заходе, увидела общий суффикс, сгруппировала по нему и забанила префиксом. Один шаблон — 500 адресов в мусор.
Решение: разнесли пул по нескольким /48 разных провайдеров, для каждого адреса завели PTR, имитирующий eyeball-имена (`broadband-{hash}.isp-{n}.example`), часть адресов оставили вообще без PTR — как у мобильных операторов. Бан упал до 15%. Оставшиеся баны — по поведенческим факторам, не по DNS.
Кейс: утечка через SNI и PTR вместе
Сервис на nginx 1.24 с TLS-терминацией. Клиенты ходили через IPv6-прокси. В логах заметили корреляцию: адреса с PTR `*.ddos-guard.example` и `*.ovh.example` давали одинаковый JA3-отпечаток и одинаковый порядок HTTP/2-заголовков.
Причина: прокси-провайдер не менял TLS-стек между адресами. PTR подтвердил, что все адреса — из одной фермы, а JA3 показал, что и клиентское ПО одно. Два независимых сигнала сложились в один вывод.
Решение со стороны провайдера — рандомизировать PTR и TLS-фрейминг по адресам. Со стороны сервиса — не блокировать по одному PTR, но понижать trust-score при совпадении PTR-паттерна с известными фермами. На практике это дало снижение ложных срабатываний на 40%: реальные пользователи за CGNAT мобильных операторов тоже имеют шаблонные PTR, но у них другой TLS-профиль.
Кейс: PTR отсутствует — и это палит
Обратная история. Прокси-сервис настроил пул так, что PTR не было вообще. Расчёт был на «нет записи — нет информации». Вышло наоборот.
Антифрод-правило: если IPv6-адрес не имеет PTR и при этом не принадлежит известному eyeball-диапазону (проверка по ASN мобильных и residential-провайдеров) — повысить риск. Мобильные операторы почти всегда имеют PTR на IPv6. Датацентры — часто нет. Отсутствие PTR у адреса из хостинг-ASN — красный флаг.
Решение: завели PTR для 80% адресов, оставив «дыры» в паттерне для правдоподобия. Для residential-имитации использовали имена вида `{hex}.dynamic.{isp}.example`, для мобильных — `{hex}.mobile.{operator}.example`. Разница в латентности DNS при этом нулевая, а детект по отсутствию PTR перестал срабатывать.
Как проверить свой пул на «фон»
Минимальный чек-лист перед запуском.
Прогнать 30–50 адресов из пула через `dig -x` и глазами посмотреть на паттерны. Если больше половины имён имеют общий суффикс или общий префикс-шаблон — пул светится. Проверить, что PTR не содержит слов `proxy`, `vpn`, `vps`, `hosting`, `static`, `pool`, `node`, `farm`. Сверить ASN через `whois -h whois.cymru.com` — если весь пул в одной ASN хостинга, это тоже сигнал.
Второй шаг — проверить, что PTR резолвится быстро и стабильно. Если ваш DNS-провайдер отдаёт NXDOMAIN с задержкой 2 секунды, антифрод-система на той стороне получит таймаут и занесёт адрес в серую зону. Третье — сверить forward-confirmed reverse DNS (FCrDNS): PTR должен резолвиться обратно в тот же IP. Несовпадение — ещё один флаг.
Настройка PTR на своей зоне
Если у вас делегирован блок (например, /48 от хостера), обратную зону настраиваете сами. У хостера запрашиваете делегирование `ip6.arpa`-зоны для вашего префикса, дальше — обычные PTR-записи в вашем DNS.
Пример зоны BIND для /64 `2001:db8:abcd:1::/64`:
```
\$ORIGIN 1.0.0.0.d.c.b.a.8.b.d.0.1.0.0.2.ip6.arpa.
\$TTL 3600
@ IN SOA ns1.example. hostmaster.example. (2024010101 7200 3600 1209600 300)
@ IN NS ns1.example.
@ IN NS ns2.example.
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR broadband-a1b2.isp-example.net.
2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR broadband-c3d4.isp-example.net.
```
TTL 3600 — разумный дефолт. Меньше ставить смысла нет: PTR меняется редко, а лишние запросы бьют по резолверам. Больше — риск, что при смене пула старые записи будут висеть в кэшах сутки.
Что делать, если PTR у чужого пула
Если вы арендуете IPv6 у провайдера и не контролируете обратную зону — попросите делегирование или хотя бы кастомизацию PTR. Многие хостеры дают это в панели. Если не дают — меняйте хостера. Пул без контроля PTR — это пул, который светится чужим шаблоном.
Проверка простая: `dig -x <ваш_ip> +short`. Если вернулось что-то вроде `host-123-45.provider.example` и вы не можете это изменить — считайте, что вас опознали. Для скрейпинга, тестов, сбора данных это критично. Для внутренних задач — по барабану.
И последнее. PTR — не единственный канал утечки, но самый дешёвый для того, кто вас проверяет. DNS-запрос стоит копейки, кэшируется, работает без вашего участия. Если пул не продуман на этом уровне, остальные меры — рандомизация отпечатков, ротация User-Agent, распределение по ASN — работают вполсилы. Начните с PTR, дальше проще.