IPv6-прокси и SNI-фильтрация: почему сайт видит реальный IP при использовании HTTPS
Содержание
Разбор полётов
Держим в голове простую схему. Браузер → прокси → сайт. Пользователь думает, что прокси скрывает его адрес. Но сайт упрямо видит реальный IP. Знакомая ситуация? Особенно с IPv6-прокси.
Проблема не в прокси. Проблема в том, как работает HTTPS и что именно передаётся в открытом виде. SNI — Server Name Indication — расширение TLS, которое сообщает серверу имя хоста до установки шифрованного соединения. Это поле открыто. Его видит весь путь следования пакета.
Как работает SNI
TLS-рукопожатие начинается с ClientHello. Внутри этого сообщения — поле SNI с доменным именем. Например, `example.com`. Это поле не шифруется. Оно нужно для того, чтобы сервер понял, какой из сотен виртуальных хостов поднимать.
```
ClientHello
...
extensions:
server_name: example.com
```
Любой промежуточный узел читает это поле. Прокси, DPI-система провайдера, балансировщик — все видят, куда вы идёте. И это нормально. Так работает интернет.
Где теряется анонимность
Предположим, вы используете IPv6-прокси от lexic.ml. Соединение идёт через прокси. Прокси подставляет свой IP в заголовки TCP. Сайт видит IP прокси. Но вот загвоздка: если прокси не умеет корректно работать с HTTPS-трафиком, он работает в режиме CONNECT-туннеля.
```
curl -x http://proxy:8080 https://example.com
```
В этом случае прокси просто открывает TCP-туннель. Внутри туннеля — TLS. Прокси не видит содержимое. Но он видит SNI. И, что важнее, если прокси использует IPv6, а сайт поддерживает только IPv4, происходит двойной переброс.
Проблема с DNS
Частый сценарий. Браузер делает DNS-запрос. Если DNS идёт мимо прокси, браузер получает реальный IP сайта. Но это не раскрывает ваш адрес. А вот если прокси делает DNS-запрос от вашего имени, а потом устанавливает соединение с реальным IP — всё в порядке.
Опасность в другом. Некоторые прокси-серверы передают заголовок `X-Forwarded-For`. Это заголовок HTTP, который добавляет промежуточный сервер. Если прокси настроен неправильно, сайт видит:
```
X-Forwarded-For: 2001:db8::1234
```
И это ваш реальный адрес. Прокси не удалил заголовок, который выставил ваш браузер или предыдущий прокси.
Технические детали
Разберём на примере. Пользователь с адресом `2001:db8:1234::5678` использует прокси с адресом `2001:db8:aaaa::1`. Запрос к `example.com`:
```bash
curl -v -x http://[2001:db8:aaaa::1]:8080 https://example.com
```
Прокси получает запрос. Он видит CONNECT-запрос:
```
CONNECT example.com:443 HTTP/1.1
Host: example.com:443
```
Прокси устанавливает соединение с `example.com:443`. Сайт видит IP прокси. Но если прокси неправильно обрабатывает заголовки, он может добавить:
```
X-Forwarded-For: 2001:db8:1234::5678
```
Сайт логирует это. И посетитель раскрыт.
Кейс с nginx
Пример из реальной практики. Nginx 1.24 с конфигурацией:
```nginx
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP \$remote_addr;
}
}
```
Директива `\$proxy_add_x_forwarded_for` добавляет IP клиента в конец существующего заголовка. Если прокси уже добавил ваш IP, nginx добавит его снова. Получается:
```
X-Forwarded-For: 2001:db8:1234::5678, 2001:db8:aaaa::1
```
Бэкенд парсит первую запись. Видит ваш реальный IPv6. Анонимность сломана.
ECH и другие решения
Encrypted Client Hello (ECH) — расширение TLS, которое шифрует SNI. Работает через DNS. Клиент получает публичный ключ сервера через DNS-запись. Шифрует ClientHello целиком.
Но ECH ещё сырой. Поддержка есть в Firefox и Chrome за флагом. На серверной стороне — единицы. Да и DNS-запросы остаются открытыми. Провайдер видит, к какому DNS-серверу вы обращаетесь.
Практический совет: если нужна анонимность в HTTPS, используйте цепочку прокси. Первый прокси принимает ваш трафик, второй — работает с сайтом. Тогда даже если первый прокси добавит `X-Forwarded-For`, второй его перезапишет.
Кейс с IPv6 и MTU
Ещё одна грабля. IPv6 имеет фиксированный MTU 1280 байт для минимальной поддержки. Но многие сайты отдают пакеты с MTU 1500. При использовании туннелей и прокси возникает фрагментация.
Пример: прокси на IPv6, сайт на IPv4. Прокси делает NAT64. Пакет 1500 байт разбивается на два. При этом TCP-сегмент теряет опцию MSS. Некоторые сайты сбрасывают такие соединения. Пользователь видит ошибку, а в логах сайта — IP прокси. Но если соединение устанавливается, сайт может видеть реальный IP в TCP-опциях.
```
TCP options: MSS 1400, SACK permitted, Timestamps
```
Если прокси не переписывает TCP-опции, реальный MSS клиента просачивается. По нему можно идентифицировать клиента. Это не раскрытие IP, но утечка информации.
Кейс с HTTP/2 и CONNECT
HTTP/2 принёс новую проблему. Extended CONNECT — метод для туннелирования произвольных протоколов. Прокси, работающие с HTTP/2, могут передавать заголовки `:authority` и `:scheme`. Если прокси не фильтрует эти поля, сайт получает:
```
:authority: example.com
:scheme: https
```
А вместе с ними — IP в заголовке `x-forwarded-for`. Причём формат может быть разным: `x-forwarded-for`, `x-real-ip`, `forwarded`. Каждый прокси добавляет свой заголовок. Сайт собирает их все.
Как проверить, видит ли сайт ваш IP
Простой способ — использовать сервис типа `ifconfig.co` или `ipinfo.io`. Но они показывают IP, с которого пришёл запрос. Если прокси работает правильно, вы увидите IP прокси. Если нет — свой.
Более точная проверка — посмотреть на заголовки, которые получает сервер. Напишем простой скрипт:
```python
import asyncio
from aiohttp import web
async def handler(request):
headers = dict(request.headers)
return web.json_response({
'remote': request.remote,
'headers': headers
})
app = web.Application()
app.router.add_get('/', handler)
web.run_app(app, port=8080)
```
Запустите его на сервере. Обратитесь через прокси. Посмотрите, что в `request.remote` и в заголовках. Если там ваш IPv6 — прокси не работает.
Настройка прокси правильно
Если вы администрируете прокси, вот минимальный набор правил:
```bash
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 3128
```
Удаляйте заголовки `X-Forwarded-For` на входе:
```nginx
proxy_set_header X-Forwarded-For "";
```
И не забывайте про IPv6. Если прокси работает на IPv6, а сайт на IPv4, нужен NAT64. Иначе соединение просто не установится.
Итоги
SNI — открытое поле. Его видно. Но SNI не раскрывает ваш IP. Ваш IP раскрывают кривые настройки прокси, лишние заголовки и невнимательность. IPv6-прокси от lexic.ml работают корректно, но только если вы не передаёте лишних заголовков сами.
Проверяйте свои запросы. Смотрите, какие заголовки уходят. И помните: даже с идеальным прокси ваш трафик виден на уровне DNS и SNI. Полная анонимность в HTTPS — это миф. Можно только минимизировать утечки.