Почему прокси на CGNAT-адресах детектируются быстрее обычных: разбор операторских NAT
Содержание
- Что такое CGNAT и почему он стал нормой
- Анатомия операторского NAT
- Почему детекторы любят CGNAT
- Как это выглядит в коде детектора
- Разница между резидентным прокси и CGNAT
- Реальные случаи
- Почему прокси на CGNAT палятся быстрее обычных
- Что делать, если вы за CGNAT
- Как отличить CGNAT от обычного прокси
- Смена стратегии детекции
Что такое CGNAT и почему он стал нормой
Операторы сотовой связи и мелкие провайдеры давно сидят без публичных IPv4. IANA раздала последние блоки ещё в 2011 году, RIPE — в 2019-м. Новым абонентам выдают адрес из диапазона 100.64.0.0/10 — это RFC 6598, пространство для carrier-grade NAT. Один такой адрес обслуживает сотни, иногда тысячи клиентов одновременно.
С точки зрения клиента всё работает: интернет есть, сайты открываются. С точки зрения сервера, который видит входящие соединения, картина совсем другая. Один адрес — это не один пользователь. Это толпа. И детекторы это знают.
Проблема в том, что многие сервисы строят защиту на предположении «один IP = один человек». Для жилых сетей с публичными адресами это ещё как-то работает. Для CGNAT — ломается полностью. Антифрод видит, что с одного адреса за минуту пришло 400 запросов от разных сессий, и блокирует всех скопом.
Анатомия операторского NAT
Внутри сети оператора абоненты получают приватные адреса — обычно 10.x.x.x. На границе стоит NAT-шлюз, который подменяет source-адрес и порт. Один публичный (или CGNAT) адрес держит пул портов. Типичная конфигурация — 2000-4000 портов на абонента, но у дешёвых операторов бывает и 512.
Порты переиспользуются. Когда сессия закрывается, порт возвращается в пул и через секунды уходит другому абоненту. Это критично для детекции: два разных человека на одном IP могут выглядеть как один нестабильный клиент, у которого за 30 секунд сменился TLS-отпечаток, User-Agent и часовой пояс.
Вот как выглядит реальная картина из логов веб-сервера:
```
100.64.18.42 - - [12/Mar/2025:14:22:01] "GET /api/feed HTTP/2.0" 200 4213
100.64.18.42 - - [12/Mar/2025:14:22:01] "GET /api/feed HTTP/2.0" 200 4213
100.64.18.42 - - [12/Mar/2025:14:22:02] "POST /api/login HTTP/2.0" 403 89
100.64.18.42 - - [12/Mar/2025:14:22:02] "GET /cart HTTP/2.0" 200 12044
100.64.18.42 - - [12/Mar/2025:14:22:03] "GET /api/feed HTTP/2.0" 200 4213
```
Пять запросов за две секунды, три разных сценария, один адрес. Для антифрода это либо бот, либо скрипт, либо фрод-ферма. На самом деле — три абонента в одной вышке.
Почему детекторы любят CGNAT
Есть несколько сигналов, которые антифрод-системы считывают почти мгновенно.
Первый — репутация диапазона. Блоки 100.64.0.0/10 давно в чёрных списках у большинства крупных сервисов. Не потому что все CGNAT-адреса плохие, а потому что доля абьюза там выше в разы. Один оператор с плохой модерацией портит репутацию всему диапазону.
Второй — плотность сессий. Если с одного IP одновременно активны 50+ TLS-сессий с разными JA3-отпечатками, это не домашний роутер. Домашний роутер держит 5-15 устройств, и отпечатки у них повторяются (одна семья, одни устройства). В CGNAT — сотни уникальных отпечатков.
Третий — геолокация. База MaxMind для CGNAT-диапазонов часто указывает на дата-центр оператора, а не на город абонента. Расхождение между заявленной геолокацией и реальной даёт ещё один флаг.
Четвёртый — поведенческие паттерны. Пять разных «пользователей» на одном IP заходят с одинаковым интервалом, используют похожие тайминги, обращаются к одним и тем же эндпоинтам. Это уже не совпадение.
Как это выглядит в коде детектора
Простейший детектор CGNAT-абьюза на Python выглядит так:
```python
import ipaddress
from collections import defaultdict
from datetime import datetime, timedelta
CGNAT = ipaddress.ip_network("100.64.0.0/10")
def analyze(log_lines):
sessions = defaultdict(set)
timestamps = defaultdict(list)
for line in log_lines:
ip, ja3, ts = parse_line(line)
if ipaddress.ip_address(ip) in CGNAT:
sessions[ip].add(ja3)
timestamps[ip].append(ts)
for ip, ja3_set in sessions.items():
window = timestamps[ip][-1] - timestamps[ip][0]
if len(ja3_set) > 20 and window < timedelta(minutes=5):
print(f"{ip}: {len(ja3_set)} fingerprints in {window}")
```
Порог в 20 отпечатков за 5 минут — эмпирический. У жилых сетей редко бывает больше 8-10 уникальных JA3 за такой интервал. У CGNAT — легко 50-100. Порог можно поднять, но тогда вырастут ложные срабатывания на публичных Wi-Fi.
Разница между резидентным прокси и CGNAT
Тут часто путаница. Резидентный прокси — это тоже чужой IP, но архитектура другая.
| Параметр | Резидентный прокси | CGNAT оператора |
|---|---|---|
| Порт на абонента | 1 выделенный | 512-4000 общих |
| Смена IP | по расписанию | при переподключении |
| Число сессий на IP | 1-5 | 50-500 |
| Геолокация | точная, по устройству | приблизительная |
| Репутация диапазона | средняя | низкая |
Резидентные прокси-провайдеры обычно держат одну сессию на устройство и меняют IP по запросу. CGNAT не даёт такой ротации. Абонент сидит на одном адресе часами, а порт меняется каждые несколько минут.
Реальные случаи
Пример: сервер на nginx 1.24 с rate-limit 100 r/m по IP начал блокировать легитимных пользователей мобильного оператора. Логи показали 340 запросов с адреса 100.64.88.17 за минуту. При этом User-Agent разный, cookies разные, поведение разное. Причина — три абонента на одной вышке одновременно открыли приложение. Решение: переход с лимита по IP на лимит по комбинации IP + cookie + JA3. Ложные блокировки упали с 12% до 0.8%.
Пример: антифрод платёжной системы отклонил 47 транзакций с CGNAT-адреса за час. Все транзакции — от разных аккаунтов, разные карты, разные суммы. Система увидела «ферму» и сработала. На деле — торговый центр, все клиенты через один Wi-Fi оператора. Причина ложного срабатывания — отсутствие анализа портов источника. Решение: учитывать source port в скоринге. Разные порты = разные устройства за NAT.
Пример: сервис на Cloudflare с WAF в режиме «high security» начал отдавать challenge всем запросам с 100.64.0.0/10. Cloudflare использует репутацию ASN. Оператор попал в серый список из-за ботнета на его сети. Решение: обращение в поддержку Cloudflare с доказательствами легитимности, либо переезд на собственный прокси-слой. Второй вариант дешевле — аренда пула адресов и маршрутизация через них.
Почему прокси на CGNAT палятся быстрее обычных
Обычный прокси — это один IP, одна сессия, один отпечаток. Детектор смотрит на репутацию адреса и поведение. Если адрес чистый, а поведение человеческое — прокси живёт долго.
CGNAT-прокси тащит за собой весь багаж оператора. Даже если конкретный абонент ведёт себя идеально, соседи по NAT портят картину. Детектор видит не поведение одного клиента, а агрегат по сотням. И агрегат почти всегда выглядит как бот-ферма.
Плюс есть техническая деталь: TTL и TCP timestamps. Устройства за одним CGNAT имеют разные TTL из-за разного числа хопов. Если с одного IP приходят пакеты с TTL 54, 57 и 61 одновременно — это точно NAT, а не один хост. Детекторы это ловят.
Что делать, если вы за CGNAT
Если ваш сервис сам находится за CGNAT (например, домашний сервер у мобильного оператора), есть несколько путей. Первый — туннель через VPS с публичным IP. Второй — IPv6, если оператор его даёт (у большинства крупных российских операторов IPv6 есть с 2020-2022 годов). Третий — reverse-прокси на стороне клиента.
Настройка IPv6-туннеля через wireguard:
```bash
ip -6 addr add 2001:db8::2/64 dev wg0
ip -6 route add default dev wg0
sysctl -w net.ipv6.conf.all.forwarding=1
```
Если оператор не даёт IPv6 — остаётся только VPS. Для прокси-сервисов, которые сами работают через CGNAT, ситуация хуже: клиенты будут получать адрес оператора, а он уже в чёрных списках. Здесь помогает только аренда пула IPv4 или использование сервисов вроде lexic.ml, где IPv6-адреса выдаются из собственных блоков, а не из операторских диапазонов.
Как отличить CGNAT от обычного прокси
Есть набор быстрых проверок. Первая — whois по адресу. Если в netname стоит что-то вроде «CGNAT» или «NAT-POOL» — вопрос закрыт. Вторая — трассировка. CGNAT обычно виден по прыжку через адрес вида 100.64.x.x в traceroute. Третья — поведение портов. Если при повторных подключениях source port меняется в широком диапазоне — это NAT.
```bash
whois 100.64.88.17 | grep -iE "netname|descr|org"
traceroute -T -p 443 100.64.88.17
```
Четвёртая — TLS-фингерпринт. Если за 10 секунд с одного IP приходит 5 разных JA3 — это точно NAT. У одиночного прокси отпечаток один, если только это не ротирующий прокси с явной сменой.
Смена стратегии детекции
Умные антифрод-системы давно отказались от «один IP = один пользователь». Вместо этого используют композитные отпечатки: TLS + HTTP/2 fingerprint + набор заголовков + поведенческие метрики. IP становится одним из десятков сигналов, а не главным.
Для CGNAT это означает, что блокировка по IP должна быть последней мерой. Сначала — challenge, потом — rate-limit по композитному ключу, и только в крайнем случае — блок. Иначе вы теряете целые регионы.
Практика показывает: сервисы, которые перешли на композитные ключи, снижают ложные блокировки в 5-10 раз. Цена — усложнение логики и рост нагрузки на бэкенд. Но если у вас в трафике больше 15% CGNAT-адресов, это окупается.