Mullvad exit IPs are surprisingly identifying
Меня всегда забавляет, когда VPN-сервисы (Virtual Private Network — защищённое соединение, которое маскирует ваш реальный адрес в интернете) обещают полную анонимность. Типа «ваш IP (уникальный номер устройства в интернете, как почтовый адрес для компьютера) скрыт, вы в безопасности». Но реальность обычно сложнее. Недавно наткнулся на исследование, которое заставило меня по-другому посмотреть на Mullvad — казалось бы, один из самых приватных VPN на рынке.
Суть в том, что IP-адреса выхода (те, которые видят сайты) не такие уж случайные, как кажется. И это может выдать вас с головой.
Как работает Mullvad
Для тех кто не в курсе: Mullvad — это шведский VPN-провайдер, который делает упор на приватность. Никаких логинов, никакой подписки через email — просто сгенерировал аккаунт и пользуешься. Приятно.
У них около 578 серверов — это скромно по меркам Proton VPN с его 20 000. Но есть фишка: на каждом сервере много IP-адресов для выхода. Когда два человека коннектятся к одному серверу, они обычно получают разные публичные IP. Это нужно, чтобы не попасть под блокировку сайтов, которые не любят, когда с одного IP сидит тысяча человек.
Но вот что интересно: IP назначается не случайно при каждом подключении. Он выбирается на основе вашего WireGuard-ключа (современный протокол VPN — способ шифрования трафика). Точнее, на основе публичного ключа (pubkey — публичный ключ, уникальный идентификатор вашего VPN-соединения, как отпечаток пальца). Этот ключ меняется каждые 1-30 дней (или вообще никогда, если используете сторонний клиент).
Казалось бы, логично — привязываем IP к ключу, получаем стабильность. Но автор исследования пошёл дальше и задал вопрос: а что если каждый сервер выбирает IP независимо? Тогда комбинаций должно быть немерено.
Эксперимент
Автор написал скрипт, который менял pubkey и собирал IP-выбора для 9 серверов. За ночь накопились данные для 3650 ключей — достаточно, чтобы понять картину.
Вот что показали серверы:
СЕРВЕР ДИАПАЗОН IP # АДРЕСОВ ───────────────────────────────────────────────────────── au-syd-wg-101 103.136.147.5 - .64 60 cl-scl-wg-001 149.88.104.4 - .14 11 de-ber-wg-007 193.32.248.245 - .252 8 dk-cph-wg-002 45.129.56.196 - .226 31 fi-hel-wg-201 185.65.133.10 - .75 66 us-lax-wg-001 23.234.72.36 - .126 91 us-nyc-wg-602 146.70.168.132 - .190 59 us-sjc-wg-302 142.147.89.212 - .224 13 za-jnb-wg-002 154.47.30.145 - .155 11
Если посчитать общее количество комбинаций для этих девяти серверов, получается больше 8 триллионов. Огромное число. Должно быть достаточно, чтобы каждый ключ получал уникальную комбинацию IP.
Но вот подвох: из 3650 протестированных ключей получили только 284 уникальные комбинации. 284! Это капля в море по сравнению с триллионами.
Разгадка
Автор заметил закономерность: если взять позицию IP внутри пула и поделить на размер пула, получается примерно одинаковое число для всех серверов.
Например, для комбинации, которую он отслеживал:
СЕРВЕР ПОЗИЦИЯ РАЗМЕР ПУЛА ПРОЦЕНТИЛЬ ────────────────────────────────────────────────────── au-syd-wg-101 49 60 81.6% cl-scl-wg-001 9 11 81.8% de-ber-wg-007 7 8 87.5% dk-cph-wg-002 25 31 80.6% fi-hel-wg-201 54 66 81.8% us-lax-wg-001 74 91 81.3% us-nyc-wg-602 48 59 81.4% us-sjc-wg-302 11 13 84.6% za-jnb-wg-002 9 11 81.8%
Все они где-то в районе 81%. Это не совпадение.
Автор предположил, и это очень похоже на правду: Mullvad использует генератор случайных чисел (RNG — генератор случайных чисел, программа, которая «бросает кубик») с затравкой (seed — затравочное число, начальная точка, от которой зависит вся последовательность «случайных» чисел) на основе pubkey. То есть ключ определяет начальное число для генератора, а дальше он выбирает позицию IP в пуле.
Но вот проблема: в Rust (а Mullvad написан на Rust) функция random_range (функция в языке Rust, команда для получения случайного числа в заданном диапазоне) работает так, что само число с плавающей точкой генерируется один раз из затравки, а потом просто умножается на размер пула. То есть границы (bound) не влияют на энтропию (мера непредсказуемости — чем выше энтропия, тем сложнее угадать результат) — они просто масштабируют уже сгенерированное число.
Это как если бы вы бросали кубик один раз, а потом просто умножали результат на любое число, какое вам скажут. Кубик выдал 0.5, вы умножаете на 60 — получаете 30. Меняете пул на 100 — получаете 50. Один и тот же «кубик», разные множители.
Отсюда и 284 комбинации вместо триллионов. Все IP привязаны к одному и тому же диапазону процентилей.
Что это значит на практике
Автор сделал инструмент, который по набору IP может оценить, сколько пользователей Mullvad имеют такую же комбинацию. Возьмём пример из статьи: определённый набор IP соответствует диапазону чисел от 0.2909 до 0.2943. Это 0,34% от всех пользователей.
При 100 000 активных пользователей Mullvad — примерно 340 человек имеют такую же комбинацию IP. Не уникально, но и не случайно.
Теперь представьте модератора на форуме. Приходит новый пользователь, которого подозревают в том, что это клон заблокированного. Оба используют разные серверы Mullvad, но оба попадают в пересекающиеся диапазоны: 0.4334-0.4428 и 0.4358-0.4423. Пересечение даёт вероятность больше 99%, что это один и тот же человек.
Звучит как научная фантастика? Но теперь добавьте сюда утечки данных и запросы через правоохранительные органы. IP-логи есть у всех. Если кто-то получит доступ к логам с разных форумов или сервисов, он может коррелировать эти данные и вычислить, что за человек сидит за Mullvad.
Как защититься
Автор даёт несколько советов:
- Не переключайте серверы чаще одного раза за период жизни ключа. Каждое переключение — это новый набор IP, который можно отследить.
- Принудительно ротируйте ключ. В приложении Mullvad можно «выйти», что сгенерирует новый ключ.
Это не идеальная защита, но хотя бы что-то.
Что я думаю
Меня зацепило вот что: это не уязвимость в коде шифрования, а логическая ошибка в алгоритме выбора IP. Разработчики, вероятно, думали, что random_range каждый раз пересчитывает случайное число — и не учли, как работает Rust.
Вывод для меня простой: никакой VPN не даёт 100% анонимности. Всегда есть вектор атаки. Даже если вы доверяете сервису — доверяйте его коду, его реализации. Используйте дополнительные меры (ротация ключей, смена серверов) и не верьте маркетингу. И помните: если вы параноик, это не значит, что за вами не следят.
Ссылки
- [Оригинальное исследование](https
Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.