Назад к блогу

mitmproxy для разработчиков: перехват и анализ HTTPS-трафика с примерами кода на Python

mitmproxy — мощный инструмент для перехвата и анализа HTTPS-трафика. Разбираем установку, настройку SSL-сертификатов, написание скриптов и реальные сценарии применения.

📅5 августа 2026 г.

Отлаживаете мобильное приложение и не понимаете, какие запросы оно отправляет на сервер? Нужно протестировать поведение 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/macOS
  • mitmproxy-ca-cert.cer — для Windows
  • mitmproxy-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.