Сайт закрыт Cloudflare, Turnstile вылезает на каждом втором запросе, а вёрстка меняется раз в две недели. При этом у того же сервиса есть мобильное приложение, которое ходит в бэкенд напрямую и получает готовый JSON — без челленджей, без разметки, со стабильной схемой полей. Это и есть «скрытый API»: незадокументированный, но полностью рабочий интерфейс, которым пользуется официальный клиент.
Разбираем по шагам, как его найти с помощью mitmproxy, что делать с certificate pinning и почему на этапе масштабирования сбора без прокси всё разваливается.
Зачем вообще лезть в трафик приложения
Скрапинг веб-версии и вызов приватного API — это разные по стоимости задачи. Сравните:
- Веб. Нужен headless-браузер, обход антибота, парсинг HTML, регулярная починка селекторов. Один запрос = мегабайты трафика и секунды процессорного времени.
- Приватный API. Обычный HTTP-запрос с парой заголовков, ответ — компактный JSON с типизированными полями. Часто отдаёт больше данных, чем показывает интерфейс: внутренние идентификаторы, флаги, служебные поля.
Мобильные бэкенды исторически защищены слабее веба. Причина прозаична: антибот-платформы заточены под браузерный трафик (JS-челленджи, canvas, поведенческие сигналы), а мобильный клиент через них просто не проходил бы. Вместо этого разработчики полагаются на статический ключ приложения и TLS-пиннинг — и то, и другое снимается на локальном устройстве.
Что понадобится
- mitmproxy — HTTPS-прокси-перехватчик с открытым кодом (более 44 000 звёзд на GitHub, актуальная ветка 12.2.2 вышла в апреле 2026, требует Python 3.12+). Ставится одной командой: pip install mitmproxy. Понимает HTTP/1, HTTP/2, HTTP/3, WebSocket и сырой TCP, работает с TLS 1.2 и 1.3.
- Android-устройство или эмулятор с root. Практика показывает, что удобнее всего Android 7–11: новые версии сильно ужесточили работу с сертификатами.
- ADB для связи с устройством и Frida (pip install frida-tools) — понадобится, если приложение пиннит сертификат.
У mitmproxy три интерфейса поверх одного движка: mitmproxy (терминальный TUI), mitmweb (веб-интерфейс, удобнее для новичка) и mitmdump (headless, для скриптов и автоматизации).
Шаг 1. Поднять перехватчик
Запускаем веб-интерфейс так, чтобы он слушал внешние подключения, а не только localhost:
mitmweb --web-host 0.0.0.0
По умолчанию прокси поднимается на порту 8080. При первом старте mitmproxy создаёт собственный удостоверяющий центр и кладёт ключи в каталог ~/.mitmproxy. Там появятся четыре файла: mitmproxy-ca.pem (сертификат вместе с приватным ключом), mitmproxy-ca-cert.pem (только сертификат), mitmproxy-ca-cert.p12 для Windows и mitmproxy-ca-cert.cer — формат для Android.
Шаг 2. Направить устройство через прокси
В настройках Wi-Fi на телефоне выбираем ручной прокси: IP вашего компьютера в локальной сети и порт 8080. Дальше открываем в браузере устройства специальный домен mitm.it — это встроенная в mitmproxy страница, которая сама определяет платформу и отдаёт нужный формат сертификата с инструкцией.
На iOS процедура состоит из трёх частей, и вторую половину все забывают: скачать профиль через Safari, установить его в «Настройки → Основные → VPN и управление устройством», а затем отдельно включить полное доверие в «Настройки → Основные → Об этом устройстве → Доверие сертификатам». Без последнего шага сертификат установлен, но не работает.
Если возиться с настройками Wi-Fi не хочется, у mitmproxy есть режим VPN-сервера: mitmweb --mode wireguard. Устройство подключается штатным клиентом WireGuard, и трафик перехватывается прозрачно, без ручной настройки прокси в системе.
Шаг 3. Главная стена — доверие сертификату
Здесь ломается большинство попыток. Проблем ровно две, и это разные проблемы.
Пользовательские CA не в почёте с 2016 года
Начиная с Android 7 Nougat (API 24) приложения по умолчанию доверяют только системному хранилищу сертификатов. Пользовательский CA игнорируется, если разработчик явно не разрешил его в Network Security Config — через блок <certificates src="user" /> в trust anchors. Это было сознательное решение Google по сокращению поверхности атаки, и обойти его настройками телефона нельзя. Chrome, кстати, пользовательским сертификатам не доверяет тоже. В Android 11 ограничения ужесточили ещё сильнее.
Практический вывод: на устройстве с root сертификат mitmproxy нужно класть в системное хранилище, а не в пользовательское. Именно поэтому root в списке требований, а не «желательно».
Certificate pinning
Вторая стена — пиннинг: приложение носит в себе отпечаток ожидаемого сертификата сервера и отказывается разговаривать с кем-либо ещё. Даже системный CA тут не спасает. Исследование ACM 2022 года показало, что пиннинг распространён в «высокорисковых» вертикалях (банки, такси, крипта), но реализован часто неполно и потому обходится.
Инструментов под задачу несколько, и они решают её по-разному:
- Frida — правка поведения в рантайме: хукаем функции проверки сертификата и заставляем их возвращать успех. Приложение при этом не модифицируется — самый гибкий вариант. Типичный запуск: frida -U -f com.target.app -l ssl_bypass.js --no-pause.
- apk-mitm — автоматически вырезает пиннинг из APK-файла статически.
- android-unpinner — пересобирает APK, внедряя Frida и скрипты снятия пиннинга.
- objection — тулкит поверх Frida, умеет и iOS, и Android.
- ssl-kill-switch2 — отключает пиннинг в приложениях iOS и macOS.
Если конкретный домен пиннится намертво и мешает работать, его можно просто исключить из перехвата опцией ignore_hosts (принимает регулярное выражение) — трафик пойдёт мимо mitmproxy без расшифровки.
Шаг 4. Найти нужный запрос
Дальше — рутина. Открываем приложение, выполняем ровно одно осмысленное действие (открыть карточку товара, пролистать ленту, применить фильтр) и смотрим, какие запросы появились. В терминальном интерфейсе это делается быстро: Z очищает список потоков, Enter открывает выбранный запрос, E экспортирует его — в том числе готовой командой curl.
Что искать в перехваченном запросе:
- Эндпоинт и параметры. Часто их заметно больше, чем задействует интерфейс приложения.
- Ключ клиента. Классика жанра — статический идентификатор, зашитый в приложение. В известном разборе публичного API MyAnimeList таким ключом оказался заголовок x-mal-client-id со значением 6591a087c62b3e94d769cd8e35ffe909, открывавший доступ к эндпоинтам api.myanimelist.net/v3/anime/season и /v3/anime с двумя десятками параметров.
- User-Agent. У мобильных клиентов он специфический и служит частью «пропуска» — в том же примере это MAL (ios, 139).
- Токены и их срок жизни. Сразу смотрите, статичен ключ или обновляется: от этого зависит вся дальнейшая архитектура сборщика.
Экспортированный curl удобно превратить в код через curlconverter — получите готовый запрос на requests, и дальше уже работаете с обычным HTTP-клиентом, без всякого браузера.
Шаг 5. Масштабирование — и где всё ломается
На этом месте наступает разочарование, знакомое всем, кто пробовал: с одного домашнего IP приватный API прекрасно отвечает первые полчаса, а потом начинает отдавать 429 и 403. Мобильные бэкенды слабее защищены от подделки клиента, но лимиты по IP там жёстче — сервер исходит из того, что за адресом сидит один телефон, а не парсер в двадцать потоков.
Отсюда практические выводы.
- Держите профиль запросов правдоподобным. Реальное приложение не делает 50 запросов в секунду и не ходит строго по расписанию. Порядок вызовов тоже имеет значение: настоящий клиент сначала запрашивает конфигурацию сессии, потом контент.
- Разносите нагрузку по адресам. Один IP = один «телефон». Про стратегии ротации, задержки с джиттером и экспоненциальный бэкофф подробно разбирали в материале как обойти API rate limiting при парсинге через прокси.
- Учитывайте гео. Многие мобильные API отдают разный контент и разные цены в зависимости от страны адреса — это одновременно и ограничение, и возможность.
Отладку удобно вести, не выходя из mitmproxy: он умеет цепляться к вышестоящему прокси. Команда mitmdump --mode upstream:http://example.com:8081 заворачивает весь трафик в апстрим, а авторизация к нему задаётся опцией --upstream-auth в формате username:password. Так вы видите те же запросы, что и раньше, но уходят они уже с внешнего адреса — можно сразу проверить, как API реагирует на конкретную страну или тип IP.
Какой тип прокси брать под мобильный API
Выбор здесь не абстрактный, он вытекает из того, кем вы притворяетесь.
- Мобильные прокси — приоритетный вариант. Вы имитируете трафик приложения, и адрес сотового оператора выглядит для бэкенда абсолютно органично: за одним адресом благодаря CGNAT реально сидят сотни абонентов, поэтому и лимиты к таким IP мягче. Подходят мобильные прокси 4G/LTE.
- Резидентные прокси — рабочая середина, если объёмы большие, а привязка к оператору не критична: домашние IP провайдеров дают широкое покрытие по гео при адекватной цене. Это резидентные прокси.
- Датацентровые — только для эндпоинтов без серьёзной проверки репутации адреса. Их ASN опознаётся мгновенно, и на мобильном бэкенде это выглядит странно: телефонов в дата-центрах не бывает.
Подводные камни, о которых узнают поздно
- HTTP/3. Поддержка QUIC в mitmproxy есть и включена по умолчанию, но на реальном мобильном трафике она ограничена: часто соединение приходится принудительно откатывать на HTTP/2 через манипуляции с ALPN. Лучше всего QUIC работает в reverse- и WireGuard-режимах.
- Приватный API меняется без предупреждения. У него нет обязательств по обратной совместимости — это внутренний интерфейс. Версия приложения в User-Agent однажды перестанет обслуживаться, и сборщик молча начнёт получать пустые ответы. Мониторьте не только коды ответов, но и структуру JSON.
- Не путайте «нашёл ключ» и «получил право». Статический ключ клиента — не разрешение на неограниченный сбор.
О правовой стороне
Перехват трафика на собственном устройстве — законная и повседневная практика отладки, ей пользуются мобильные разработчики и специалисты по безопасности. Границы начинаются дальше: соблюдайте условия использования сервиса, не собирайте персональные данные без правового основания (в ЕС это прямо регулируется GDPR), не трогайте эндпоинты, требующие чужой авторизации, и держите нагрузку на уровне, который не мешает работе сервиса. Практический ориентир: если данные видны в приложении любому пользователю без входа в аккаунт — вы в относительно спокойной зоне; если для доступа нужна чужая учётная запись — вы уже за её пределами.
Коротко
Схема рабочая и экономит недели возни с антиботом: поднимаем mitmproxy, кладём сертификат в системное хранилище устройства с root, при необходимости снимаем пиннинг через Frida, ловим один осмысленный запрос, экспортируем его в curl и переписываем на Python. Дальше задача из категории «обойти защиту» превращается в задачу «аккуратно распределить нагрузку» — и решается ротацией адресов, разумными паузами и правильным типом прокси. Начать проще всего с мобильных прокси: они ближе всего к тому трафику, который бэкенд ожидает увидеть.
