В начале апреля пользователи начали массово жаловаться на странное поведение крупных российских сервисов: Wildberries, Ozon, «Сбер», а также ряд других приложений и сайтов стали работать нестабильно при включённом VPN.
Сценарий выглядит знакомо: сайт открывается, логин проходит, а дальше не грузятся карточки товаров, не открываются изображения, ломается оформление заказа, приложение может показать «ошибку сети».
По данным свежих публикаций, это уже не случайные сетевые сбои, а целенаправленная фильтрация VPN-трафика, которую крупные платформы начали внедрять после рекомендаций регулятора.
Но для разработчика здесь интереснее другой вопрос — как приложение вообще определяет, что на устройстве включён VPN?
На первый взгляд кажется, что это почти магия. На практике это вполне приземлённый стек из системных API, сетевого анализа и backend-антифрода.
Разберём по слоям.
1. Почему это вообще стало возможно
Сначала важно понять: блокировка VPN сегодня — это не обязательно блокировка на уровне провайдера.
Сейчас всё чаще используется другая схема: сам сервис определяет VPN и ограничивает доступ со своей стороны.
То есть не оператор режет трафик, не DPI ломает соединение, а само приложение или backend принимает решение.
И вот тут начинается самое интересное.
2. Проверка VPN прямо на устройстве
На Android при включении VPN система создаёт виртуальный сетевой интерфейс, обычно это tun0, ppp0, wg0, ipsec0.
Если приложение получает список интерфейсов, оно мгновенно видит этот tunnel.
val interfaces = NetworkInterface.getNetworkInterfaces().toList()val vpnEnabled = interfaces.any { it.name.startsWith("tun") || it.name.startsWith("ppp") || it.name.startsWith("wg")}
Никакой магии здесь нет, просто проверка интерфейсов.
На практике это один из самых старых и до сих пор рабочих способов, особенно для Android-приложений банков и маркетплейсов.
3. Более современный способ: системный API
Парсить интерфейсы — это уже немного old school.
Сегодня чаще используют системный API.
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManagerval network = cm.activeNetworkval caps = cm.getNetworkCapabilities(network)val isVpn = caps?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) == true
Это production-friendly вариант: он надёжнее, не зависит от имени интерфейса, работает стабильнее между устройствами.
Для enterprise-приложений это фактически стандартный путь.
4. Backend-детектирование без доступа к устройству
Вот это ключевая мысль, которую часто упускают.
VPN можно определить вообще без доступа к устройству, исключительно на стороне backend.
И чаще всего именно так и делают.
Допустим, пользователь открыл Ozon. Приложение отправляет HTTPS-запрос, сервер видит IP, и этого уже достаточно.
Backend проверяет ASN, страну IP, принадлежность датацентру, IP reputation, известные exit nodes.
if request.ip.asn in VPN_ASN_BLACKLIST: return block_response()
Например, если IP принадлежит Hetzner, M247, DigitalOcean, OVH, AWS, то вероятность того, что это VPN или proxy, очень высока.
5. Гео-несоответствие как сильный сигнал
Это особенно любят антифрод-системы.
Представим: GPS телефона показывает Москву, часовой пояс устройства — Europe/Moscow, язык системы — ru-RU, SIM-карта российского оператора, а IP определяется как Нидерланды.
Для backend это почти textbook case.
Система повышает fraud score.
score = 0if ip.country != device.geo_country: score += 50if timezone != ip.timezone: score += 20if asn in KNOWN_VPN_ASN: score += 40
Если score превышает порог, доступ ограничивается.
И пользователь видит лишь то, что «не грузятся картинки».
6. Почему часто ломаются именно карточки товаров
Это очень показательный симптом.
По сообщениям пользователей, чаще всего открывается главная, работает поиск, но ломаются карточки, не загружаются изображения, не проходит checkout.
Это означает, что блокировка, вероятно, не на уровне всего домена.
Скорее используется селективная фильтрация API-эндпоинтов, когда /auth остаётся доступным, /catalog/item уже ограничивается, а /checkout блокируется ещё жёстче.
То есть сервис не хочет показывать пользователю явную ошибку, а предпочитает мягкий отказ, когда часть функциональности просто перестаёт работать.
7. Fingerprint трафика
Здесь уже начинается то, что особенно интересно сетевикам и ИБ.
VPN можно детектить по самому трафику: по TLS fingerprint, JA3 и JA4, MTU patterns, packet size distribution, latency anomalies.
Например, WireGuard и OpenVPN часто оставляют узнаваемые сетевые паттерны даже при маскировке.
На масштабе маркетплейса с миллионами пользователей это вполне рабочий метод.
8. Главная проблема — false positive
Главная инженерная боль здесь — ложные срабатывания.
Отличить VPN от легитимного корпоративного трафика невероятно сложно.
Сотрудник может сидеть через корпоративный proxy, мобильный оператор может использовать CGNAT, CDN может менять маршруты, пользователь может находиться в международном роуминге.
Всё это внешне очень похоже на VPN.
И именно здесь легко случайно начать блокировать честных пользователей.
9. Вывод
Когда кажется, что приложение «как-то узнало», что включён VPN, это не магия.
Обычно это комбинация вполне стандартных инженерных техник: проверка TRANSPORT_VPN, анализ IP, anti-fraud scoring, сетевой fingerprint.
Именно поэтому у пользователя может иногда работать всё, иногда ломаться только checkout, иногда блокироваться только часть API.