Отлаживаете мобильное приложение и не понимаете, какие запросы оно отправляет на сервер? Нужно протестировать поведение API под разными условиями или перехватить ответ и подменить данные на лету? mitmproxy решает все эти задачи — это бесплатный инструмент с открытым исходным кодом, который позволяет полностью контролировать HTTP и HTTPS-трафик между клиентом и сервером.
Что такое mitmproxy и зачем он нужен разработчику
mitmproxy — это интерактивный MITM-прокси (Man-In-The-Middle Proxy) с открытым исходным кодом, написанный на Python. Он работает как посредник между вашим приложением и сервером: перехватывает все запросы и ответы, позволяет их просматривать, изменять, воспроизводить и сохранять.
Главное отличие mitmproxy от обычных прокси-серверов — возможность работать с зашифрованным HTTPS-трафиком. Инструмент динамически генерирует SSL-сертификаты для каждого домена, что позволяет расшифровывать трафик на лету, не нарушая работу приложения.
Вот типичные задачи, которые разработчики решают с помощью mitmproxy:
- Отладка API — видите точные запросы и ответы, включая заголовки, тело, коды статусов.
- Реверс-инжиниринг — анализируете, как работают сторонние приложения и сервисы.
- Тестирование — подменяете ответы сервера для проверки граничных случаев.
- Автоматизация — пишете скрипты для модификации трафика по условиям.
- Запись и воспроизведение — сохраняете сессию и воспроизводите её без реального сервера.
- Анализ безопасности — проверяете, не передаёт ли приложение лишние данные.
Важно понимать
mitmproxy — инструмент для легального тестирования и разработки. Используйте его только для анализа трафика приложений, которые вы разрабатываете или имеете право тестировать. Перехват чужого трафика без разрешения нарушает законодательство.
Инструмент поставляется в трёх вариантах: консольный интерактивный интерфейс mitmproxy, веб-интерфейс mitmweb и утилита командной строки mitmdump. Все три используют одно ядро и поддерживают Python-скрипты.
Установка mitmproxy на Windows, macOS и Linux
mitmproxy устанавливается несколькими способами. Рекомендуем использовать pip — это обеспечивает актуальную версию и простое обновление.
Установка через pip (универсальный способ)
Требуется Python 3.9 или новее. Проверьте версию Python:
python --version
# или
python3 --version
Устанавливаем mitmproxy:
pip install mitmproxy
После установки проверяем:
mitmproxy --version
# Должно вывести: mitmproxy 10.x.x
Установка через менеджеры пакетов
macOS (Homebrew):
brew install mitmproxy
Linux (Ubuntu/Debian):
sudo apt install mitmproxy
# или через snap для актуальной версии:
sudo snap install mitmproxy
Windows: Скачайте установщик с официального сайта mitmproxy.org или используйте pip в PowerShell с правами администратора.
Запуск и базовая проверка
По умолчанию mitmproxy слушает порт 8080. Запускаем веб-интерфейс для начала работы:
# Запуск веб-интерфейса на порту 8080
mitmweb
# Запуск на другом порту
mitmweb --listen-port 9090
# Консольный интерфейс
mitmproxy
После запуска mitmweb откройте браузер по адресу http://127.0.0.1:8081 — это веб-интерфейс для просмотра трафика. Сам прокси работает на порту 8080.
Настройка SSL-сертификатов для перехвата HTTPS
Перехват HTTPS-трафика требует установки корневого сертификата mitmproxy в систему или браузер. Без этого шага браузер будет показывать предупреждение о небезопасном соединении, а многие приложения вовсе откажутся работать.
Как это работает технически
При первом запуске mitmproxy автоматически создаёт корневой CA-сертификат и сохраняет его в директорию ~/.mitmproxy/. Когда клиент подключается к HTTPS-сайту через прокси, mitmproxy на лету генерирует сертификат для этого домена, подписывая его своим CA. Клиент доверяет этому сертификату, если CA добавлен в доверенные — и расшифрование происходит прозрачно.
Установка сертификата в систему
Сертификаты находятся в ~/.mitmproxy/:
mitmproxy-ca-cert.pem— для Linux/macOSmitmproxy-ca-cert.cer— для Windowsmitmproxy-ca-cert.p12— для iOS
macOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain \
~/.mitmproxy/mitmproxy-ca-cert.pem
Linux (Ubuntu/Debian):
sudo cp ~/.mitmproxy/mitmproxy-ca-cert.pem \
/usr/local/share/ca-certificates/mitmproxy.crt
sudo update-ca-certificates
Windows: Дважды кликните на файл mitmproxy-ca-cert.cer → «Установить сертификат» → «Локальный компьютер» → «Доверенные корневые центры сертификации».
Установка в браузер Firefox
Firefox использует собственное хранилище сертификатов. Перейдите: Настройки → Конфиденциальность и защита → Сертификаты → Просмотр сертификатов → Центры сертификации → Импортировать. Выберите файл mitmproxy-ca-cert.pem и отметьте «Доверять при идентификации веб-сайтов».
Проверка работы
Настройте браузер на использование прокси 127.0.0.1:8080 и откройте любой HTTPS-сайт. В интерфейсе mitmweb вы должны увидеть расшифрованный трафик. Альтернативно — откройте http://mitm.it через настроенный прокси: mitmproxy покажет инструкции по установке сертификата для вашей платформы.
Три интерфейса: mitmproxy, mitmweb и mitmdump
Пакет mitmproxy включает три утилиты с разными интерфейсами для разных сценариев работы. Понимание различий поможет выбрать правильный инструмент для каждой задачи.
| Утилита | Интерфейс | Когда использовать | Особенности |
|---|---|---|---|
mitmproxy |
Консольный TUI | Интерактивная отладка в терминале | Требует терминал с поддержкой цветов, мощный фильтр |
mitmweb |
Веб-браузер | Визуальный анализ трафика | Удобный UI, поддержка фильтров, экспорт |
mitmdump |
CLI (stdout) | Скрипты, CI/CD, автоматизация | Без интерактивности, вывод в файл или pipe |
Полезные флаги запуска
# Запись трафика в файл
mitmdump -w traffic.dump
# Воспроизведение записанного трафика
mitmdump -r traffic.dump
# Фильтрация: только запросы к определённому домену
mitmproxy --filter "~d api.example.com"
# Запуск в режиме transparent proxy
mitmproxy --mode transparent
# Запуск как upstream proxy (цепочка прокси)
mitmproxy --mode upstream:http://upstream-proxy:8080
# Указание конкретного порта
mitmweb --listen-port 9090 --web-port 9091
# Запуск со скриптом
mitmproxy -s my_script.py
Синтаксис фильтров mitmproxy
mitmproxy поддерживает мощный язык фильтров для выборки нужных запросов:
# ~d — фильтр по домену
~d api.example.com
# ~u — фильтр по URL (regex)
~u /api/v2/users
# ~m — фильтр по HTTP-методу
~m POST
# ~s — только ответы
~s ~c 404
# ~c — фильтр по коду статуса
~c 500
# Комбинирование (AND)
~d api.example.com & ~m POST
# Комбинирование (OR)
~c 404 | ~c 500
# NOT
!~d static.example.com
Написание скриптов на Python: перехват и модификация трафика
Скрипты — главная суперсила mitmproxy. С их помощью вы можете автоматически изменять запросы и ответы, логировать данные в нужном формате, имитировать ошибки сервера и многое другое. Скрипты пишутся на Python и используют событийную модель.
Основные события (хуки)
| Хук | Когда вызывается | Объект |
|---|---|---|
request |
Получен запрос от клиента | flow.request |
response |
Получен ответ от сервера | flow.response |
error |
Ошибка соединения | flow.error |
tls_start_client |
Начало TLS-рукопожатия с клиентом | tls_start |
Пример 1: логирование запросов в файл
# logger.py
import mitmproxy.http
import json
from datetime import datetime
def request(flow: mitmproxy.http.HTTPFlow) -> None:
"""Логируем каждый запрос в JSON-файл."""
log_entry = {
"timestamp": datetime.now().isoformat(),
"method": flow.request.method,
"url": flow.request.pretty_url,
"headers": dict(flow.request.headers),
"body": flow.request.text if flow.request.text else None
}
with open("requests.log", "a") as f:
f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
def response(flow: mitmproxy.http.HTTPFlow) -> None:
"""Логируем ответы с кодом ошибки."""
if flow.response.status_code >= 400:
print(f"[ERROR] {flow.request.method} {flow.request.pretty_url} "
f"-> {flow.response.status_code}")
Запуск скрипта:
mitmproxy -s logger.py
Пример 2: модификация запросов — подмена заголовков
# modify_headers.py
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
"""Подменяем User-Agent и добавляем кастомный заголовок."""
if "api.example.com" in flow.request.pretty_host:
# Подмена User-Agent
flow.request.headers["User-Agent"] = (
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) "
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1"
)
# Добавление заголовка авторизации
flow.request.headers["X-Custom-Token"] = "test-token-12345"
# Удаление заголовка
if "X-Debug-Info" in flow.request.headers:
del flow.request.headers["X-Debug-Info"]
Пример 3: подмена ответа сервера (mock)
# mock_response.py
from mitmproxy import http
import json
def request(flow: http.HTTPFlow) -> None:
"""Перехватываем запрос и возвращаем mock-ответ, не обращаясь к серверу."""
if flow.request.pretty_url.endswith("/api/v1/user/profile"):
# Создаём mock-ответ
mock_data = {
"id": 42,
"name": "Test User",
"email": "[email protected]",
"premium": True # Тестируем premium-функциональность
}
flow.response = http.Response.make(
200, # Код статуса
json.dumps(mock_data), # Тело ответа
{"Content-Type": "application/json"} # Заголовки
)
def response(flow: http.HTTPFlow) -> None:
"""Модифицируем реальный ответ сервера."""
if "/api/v1/products" in flow.request.pretty_url:
try:
data = json.loads(flow.response.text)
# Добавляем поле к каждому продукту
for product in data.get("items", []):
product["debug_info"] = "intercepted"
flow.response.text = json.dumps(data)
except (json.JSONDecodeError, KeyError):
pass
Пример 4: имитация медленного соединения и ошибок
# chaos_testing.py
from mitmproxy import http
import time
import random
def response(flow: http.HTTPFlow) -> None:
"""Chaos engineering: случайные задержки и ошибки для тестирования."""
# Добавляем случайную задержку от 0 до 2 секунд
if "api.example.com" in flow.request.pretty_host:
delay = random.uniform(0, 2.0)
time.sleep(delay)
# 10% вероятность ответа 503
if random.random() < 0.1:
flow.response = http.Response.make(
503,
json.dumps({"error": "Service Unavailable"}),
{"Content-Type": "application/json"}
)
Перехват трафика мобильных приложений
Анализ трафика мобильных приложений — одна из самых частых задач при использовании mitmproxy. Это особенно полезно при реверс-инжиниринге API мобильных приложений или тестировании собственного приложения на реальном устройстве.
Настройка на Android
Шаг 1. Убедитесь, что телефон и компьютер в одной Wi-Fi сети.
Шаг 2. Запустите mitmproxy на компьютере:
mitmweb --listen-host 0.0.0.0 --listen-port 8080
Шаг 3. На Android: Настройки → Wi-Fi → удерживайте сеть → Изменить сеть → Дополнительно → Прокси → Вручную. Введите IP компьютера и порт 8080.
Шаг 4. Установка сертификата на Android: откройте браузер на устройстве, перейдите на http://mitm.it и скачайте сертификат для Android. Затем: Настройки → Безопасность → Установить сертификат → Сертификат CA.
Android 7+ и Certificate Pinning
Начиная с Android 7.0, приложения по умолчанию не доверяют пользовательским CA-сертификатам. Для перехвата трафика таких приложений потребуется либо root-доступ, либо модификация network_security_config.xml в APK. Для приложений с SSL Pinning используйте Frida или Xposed Framework для обхода проверки сертификата.
Настройка на iOS
Шаг 1. Настройте прокси аналогично Android: Настройки → Wi-Fi → нажмите (i) рядом с сетью → Настроить прокси → Вручную.
Шаг 2. Откройте Safari и перейдите на http://mitm.it — скачайте сертификат для iOS.
Шаг 3. Установите профиль: Настройки → Загруженный профиль → Установить.
Шаг 4. Включите доверие к сертификату: Настройки → Основные → Об устройстве → Доверие сертификатам — включите переключатель для mitmproxy.
Перехват трафика конкретного приложения через Python
# mobile_app_analyzer.py
from mitmproxy import http
import json
import re
# Домены интересующего приложения
TARGET_DOMAINS = ["api.myapp.com", "cdn.myapp.com"]
def response(flow: http.HTTPFlow) -> None:
"""Анализируем трафик мобильного приложения."""
host = flow.request.pretty_host
if not any(domain in host for domain in TARGET_DOMAINS):
return
# Извлекаем JSON-ответы
content_type = flow.response.headers.get("content-type", "")
if "application/json" in content_type:
try:
data = json.loads(flow.response.text)
print(f"\n{'='*60}")
print(f"URL: {flow.request.pretty_url}")
print(f"Status: {flow.response.status_code}")
print(f"Response: {json.dumps(data, indent=2, ensure_ascii=False)}")
except json.JSONDecodeError:
pass
# Ищем токены в заголовках запроса
auth_header = flow.request.headers.get("authorization", "")
if auth_header:
print(f"[AUTH] Token found: {auth_header[:50]}...")
Цепочка прокси: mitmproxy + upstream-прокси
Один из мощных сценариев — использование mitmproxy в связке с внешним прокси-сервером. Это позволяет одновременно перехватывать и анализировать трафик (через mitmproxy) и направлять его через внешний IP-адрес (через upstream-прокси). Такая схема используется при тестировании геозависимых API или при разработке приложений, которые должны работать через прокси.
Режим upstream proxy
# Направляем весь трафик через upstream HTTP-прокси
mitmproxy --mode upstream:http://proxy-host:port
# Upstream SOCKS5 прокси
mitmproxy --mode upstream:socks5://proxy-host:port
# С аутентификацией
mitmproxy --mode upstream:http://user:password@proxy-host:port
# Через mitmweb с upstream
mitmweb --mode upstream:http://proxy-host:port
В этом режиме mitmproxy принимает запросы локально, расшифровывает HTTPS, даёт вам возможность их анализировать и модифицировать, а затем передаёт через внешний прокси. Это особенно удобно при тестировании API, доступных только из определённых регионов.
Для таких задач хорошо подходят резидентные прокси — они имеют реальные IP-адреса домашних пользователей из нужных стран, что позволяет корректно тестировать геозависимые ответы API.
Динамический выбор upstream-прокси в скрипте
# dynamic_upstream.py
from mitmproxy import http
from mitmproxy.net.server_spec import ServerSpec
# Список прокси для ротации
PROXY_LIST = [
"http://proxy1.example.com:8080",
"http://proxy2.example.com:8080",
"http://proxy3.example.com:8080",
]
proxy_index = 0
def request(flow: http.HTTPFlow) -> None:
"""Ротация upstream-прокси для каждого запроса."""
global proxy_index
# Направляем запросы к API через разные прокси
if "api.target.com" in flow.request.pretty_host:
proxy_url = PROXY_LIST[proxy_index % len(PROXY_LIST)]
proxy_index += 1
flow.live.change_upstream_proxy_server(
ServerSpec.from_url(proxy_url)
)
print(f"Using proxy: {proxy_url} for {flow.request.pretty_url}")
Прозрачный прокси (transparent mode)
В прозрачном режиме приложение не знает, что его трафик перехватывается — не нужно настраивать прокси в настройках. Это требует настройки iptables/pf на уровне ОС:
# Запуск в transparent режиме
mitmproxy --mode transparent --listen-port 8080
# Настройка iptables для перенаправления трафика (Linux)
sudo iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 8080
Практические сценарии применения mitmproxy
Рассмотрим конкретные задачи, которые разработчики решают с помощью mitmproxy в реальных проектах.
Сценарий 1: Тестирование API без изменения сервера
Представьте: вам нужно проверить, как фронтенд обрабатывает ответ с пустым массивом данных или ошибку авторизации 401, но воспроизвести это на тестовом сервере сложно. mitmproxy позволяет подменить ответ прямо на лету:
# test_edge_cases.py
from mitmproxy import http
import json
def response(flow: http.HTTPFlow) -> None:
url = flow.request.pretty_url
# Тест: пустой список товаров
if "/api/products" in url and "test_empty=1" in url:
flow.response.text = json.dumps({"items": [], "total": 0})
# Тест: истёкший токен
if "/api/user" in url and "test_auth=1" in url:
flow.response = http.Response.make(
401,
json.dumps({"error": "Token expired", "code": "AUTH_001"}),
{"Content-Type": "application/json"}
)
# Тест: превышение лимита запросов
if "/api/" in url and "test_rate=1" in url:
flow.response = http.Response.make(
429,
json.dumps({"error": "Too Many Requests", "retry_after": 60}),
{"Content-Type": "application/json",
"Retry-After": "60"}
)
Сценарий 2: Запись и воспроизведение сессии
Полезно для создания тестовых фикстур или демонстрации функциональности без реального сервера:
# Запись сессии в файл
mitmdump -w session.dump --filter "~d api.example.com"
# Воспроизведение записанной сессии (offline)
mitmdump -r session.dump
# Конвертация в HAR-формат для анализа
mitmdump -r session.dump --flow-detail 3 > session.txt
Сценарий 3: Автоматическая документация API
# api_documenter.py
from mitmproxy import http
import json
from collections import defaultdict
# Словарь для накопления информации об эндпоинтах
endpoints = defaultdict(lambda: {"methods": set(), "status_codes": set(),
"request_fields": set(), "response_fields": set()})
def _extract_fields(data, prefix=""):
"""Рекурсивно извлекаем поля из JSON."""
fields = set()
if isinstance(data, dict):
for key, value in data.items():
full_key = f"{prefix}.{key}" if prefix else key
fields.add(full_key)
fields.update(_extract_fields(value, full_key))
elif isinstance(data, list) and data:
fields.update(_extract_fields(data[0], prefix))
return fields
def response(flow: http.HTTPFlow) -> None:
if "api.example.com" not in flow.request.pretty_host:
return
# Нормализуем URL (убираем ID)
import re
path = re.sub(r'/\d+', '/{id}', flow.request.path)
endpoint = f"{flow.request.method} {path}"
ep = endpoints[endpoint]
ep["methods"].add(flow.request.method)
ep["status_codes"].add(flow.response.status_code)
# Извлекаем поля запроса
if flow.request.text:
try:
req_data = json.loads(flow.request.text)
ep["request_fields"].update(_extract_fields(req_data))
except json.JSONDecodeError:
pass
# Извлекаем поля ответа
if flow.response.text:
try:
resp_data = json.loads(flow.response.text)
ep["response_fields"].update(_extract_fields(resp_data))
except json.JSONDecodeError:
pass
def done():
"""Выводим документацию при завершении."""
print("\n=== API DOCUMENTATION ===\n")
for endpoint, info in sorted(endpoints.items()):
print(f"Endpoint: {endpoint}")
print(f" Status codes: {sorted(info['status_codes'])}")
if info["request_fields"]:
print(f" Request fields: {sorted(info['request_fields'])}")
if info["response_fields"]:
print(f" Response fields: {sorted(info['response_fields'])}")
print()
Сценарий 4: Тестирование геозависимых ответов
При разработке приложений с региональным контентом важно проверить, как API отвечает на запросы из разных стран. Для этого mitmproxy запускается в режиме upstream с прокси дата-центров из нужных регионов — это быстрый и надёжный способ имитировать запросы из конкретных стран.
# geo_test.py
from mitmproxy import http
def response(flow: http.HTTPFlow) -> None:
"""Логируем геозависимые заголовки и данные."""
# Смотрим, какой контент возвращает сервер
geo_headers = ["cf-ipcountry", "x-country", "x-geo-country"]
for header in geo_headers:
value = flow.response.headers.get(header)
if value:
print(f"[GEO] {header}: {value} | URL: {flow.request.pretty_url}")
# Ищем упоминания валют и локалей в ответе
if flow.response.text:
import re
currencies = re.findall(r'"currency":\s*"([A-Z]{3})"', flow.response.text)
locales = re.findall(r'"locale":\s*"([a-z]{2}-[A-Z]{2})"', flow.response.text)
if currencies:
print(f"[CURRENCY] {currencies}")
if locales:
print(f"[LOCALE] {locales}")
Сценарий 5: Использование mitmproxy в CI/CD
mitmdump идеально подходит для интеграционных тестов в CI/CD пайплайне — он запускается как фоновый процесс, записывает трафик и завершается вместе с тестами:
#!/bin/bash
# ci_test.sh
# Запускаем mitmdump в фоне
mitmdump -w test_traffic.dump -s ci_assertions.py &
MITM_PID=$!
# Даём прокси время запуститься
sleep 1
# Запускаем тесты с прокси
export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080
pytest tests/integration/ -v
# Останавливаем mitmdump
kill $MITM_PID
# Анализируем записанный трафик
mitmdump -r test_traffic.dump -s analyze_traffic.py
Скрипт ci_assertions.py может проверять, что приложение не делает лишних запросов, не передаёт чувствительные данные в незашифрованном виде и соблюдает контракты API.
Сценарий 6: Анализ трафика парсера
При разработке парсеров mitmproxy помогает понять, какие именно запросы делает браузер при загрузке страницы — включая XHR/fetch-запросы к API, которые не видны в исходном коде HTML. Это позволяет напрямую обращаться к API сайта вместо парсинга HTML. При разработке таких решений часто используют резидентные прокси для ротации IP, чтобы избежать блокировок при сборе данных.
Заключение
mitmproxy — один из самых мощных инструментов в арсенале разработчика для работы с HTTP/HTTPS-трафиком. Он объединяет в себе функции отладчика, тестового окружения, документатора API и инструмента для анализа безопасности. Три интерфейса — консольный, веб и CLI — покрывают все сценарии: от интерактивной отладки до автоматизации в CI/CD.