Torna al blog

mitmproxy per sviluppatori: intercettare e analizzare il traffico HTTPS con esempi di codice in Python

mitmproxy è uno strumento potente per l'intercettazione e l'analisi del traffico HTTPS. Trattiamo l'installazione, la configurazione dei certificati SSL, la scrittura di script e scenari reali di utilizzo.

📅5 agosto 2026
```html

Stai debugando un'app mobile e non capisci quali richieste sta inviando al server? Devi testare il comportamento dell'API in diverse condizioni o intercettare la risposta e modificare i dati al volo? mitmproxy risolve tutti questi problemi: è uno strumento gratuito e open source che consente di controllare completamente il traffico HTTP e HTTPS tra client e server.

Che cos'è mitmproxy e a cosa serve agli sviluppatori

mitmproxy è un proxy MITM interattivo (Man-In-The-Middle Proxy) open source, scritto in Python. Funziona come intermediario tra la tua applicazione e il server: intercetta tutte le richieste e risposte, permettendo di visualizzarle, modificarle, riprodurle e salvarle.

La principale differenza tra mitmproxy e i normali server proxy è la possibilità di lavorare con il traffico HTTPS crittografato. Lo strumento genera dinamicamente certificati SSL per ogni dominio, consentendo di decrittografare il traffico al volo, senza interrompere il funzionamento dell'applicazione.

Ecco alcune delle tipiche attività che gli sviluppatori risolvono con mitmproxy:

  • Debugging API — vedi le richieste e le risposte esatte, inclusi intestazioni, corpo e codici di stato.
  • Reverse engineering — analizzi come funzionano le applicazioni e i servizi di terze parti.
  • Testing — sostituisci le risposte del server per verificare i casi limite.
  • Automazione — scrivi script per modificare il traffico in base a condizioni.
  • Registrazione e riproduzione — salva la sessione e riproducila senza un server reale.
  • Analisi della sicurezza — verifica se l'applicazione trasmette dati non necessari.

È importante capire

mitmproxy è uno strumento per testare e sviluppare legalmente. Usalo solo per analizzare il traffico delle applicazioni che stai sviluppando o per cui hai il diritto di testare. Intercettare il traffico di terzi senza autorizzazione viola la legge.

Lo strumento è disponibile in tre varianti: interfaccia interattiva da console mitmproxy, interfaccia web mitmweb e utility da riga di comando mitmdump. Tutte e tre utilizzano un unico nucleo e supportano script Python.

Installazione di mitmproxy su Windows, macOS e Linux

mitmproxy può essere installato in diversi modi. Si consiglia di utilizzare pip — questo garantisce la versione più recente e un aggiornamento semplice.

Installazione tramite pip (metodo universale)

È richiesto Python 3.9 o superiore. Controlla la versione di Python:

python --version
# o
python3 --version

Installa mitmproxy:

pip install mitmproxy

Dopo l'installazione, controlla:

mitmproxy --version
# Dovrebbe restituire: mitmproxy 10.x.x

Installazione tramite gestori di pacchetti

macOS (Homebrew):

brew install mitmproxy

Linux (Ubuntu/Debian):

sudo apt install mitmproxy
# o tramite snap per la versione più recente:
sudo snap install mitmproxy

Windows: Scarica l'installer dal sito ufficiale mitmproxy.org o utilizza pip in PowerShell con privilegi di amministratore.

Avvio e verifica di base

Per impostazione predefinita, mitmproxy ascolta sulla porta 8080. Avvia l'interfaccia web per iniziare a lavorare:

# Avvio dell'interfaccia web sulla porta 8080
mitmweb

# Avvio su un'altra porta
mitmweb --listen-port 9090

# Interfaccia da console
mitmproxy

Dopo aver avviato mitmweb, apri il browser all'indirizzo http://127.0.0.1:8081 — questa è l'interfaccia web per visualizzare il traffico. Il proxy stesso funziona sulla porta 8080.

Configurazione dei certificati SSL per l'intercettazione HTTPS

L'intercettazione del traffico HTTPS richiede l'installazione del certificato CA radice di mitmproxy nel sistema o nel browser. Senza questo passaggio, il browser mostrerà un avviso di connessione non sicura e molte applicazioni smetteranno di funzionare.

Come funziona tecnicamente

Al primo avvio, mitmproxy crea automaticamente un certificato CA radice e lo salva nella directory ~/.mitmproxy/. Quando un client si connette a un sito HTTPS tramite il proxy, mitmproxy genera al volo un certificato per quel dominio, firmandolo con il proprio CA. Il client si fida di questo certificato se il CA è stato aggiunto ai fidati — e la decrittografia avviene in modo trasparente.

Installazione del certificato nel sistema

I certificati si trovano in ~/.mitmproxy/:

  • mitmproxy-ca-cert.pem — per Linux/macOS
  • mitmproxy-ca-cert.cer — per Windows
  • mitmproxy-ca-cert.p12 — per 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: Fai doppio clic sul file mitmproxy-ca-cert.cer → "Installa certificato" → "Computer locale" → "Autorità di certificazione radicate attendibili".

Installazione nel browser Firefox

Firefox utilizza un proprio archivio di certificati. Vai su: Impostazioni → Privacy e sicurezza → Certificati → Visualizza certificati → Autorità di certificazione → Importa. Seleziona il file mitmproxy-ca-cert.pem e spunta "Fidati per l'identificazione dei siti web".

Verifica del funzionamento

Configura il browser per utilizzare il proxy 127.0.0.1:8080 e apri qualsiasi sito HTTPS. Nell'interfaccia mitmweb dovresti vedere il traffico decrittografato. In alternativa, apri http://mitm.it tramite il proxy configurato: mitmproxy mostrerà le istruzioni per l'installazione del certificato per la tua piattaforma.

Tre interfacce: mitmproxy, mitmweb e mitmdump

Il pacchetto mitmproxy include tre utility con interfacce diverse per scenari di lavoro differenti. Comprendere le differenze aiuterà a scegliere lo strumento giusto per ogni attività.

Utility Interfaccia Quando utilizzare Caratteristiche
mitmproxy TUI da console Debugging interattivo nel terminale Richiede un terminale con supporto per i colori, potente filtro
mitmweb Browser web Analisi visiva del traffico UI intuitiva, supporto per filtri, esportazione
mitmdump CLI (stdout) Script, CI/CD, automazione Senza interattività, output in file o pipe

Flag utili per l'avvio

# Registrazione del traffico in un file
mitmdump -w traffic.dump

# Riproduzione del traffico registrato
mitmdump -r traffic.dump

# Filtraggio: solo richieste a un dominio specifico
mitmproxy --filter "~d api.example.com"

# Avvio in modalità proxy trasparente
mitmproxy --mode transparent

# Avvio come upstream proxy (catena di proxy)
mitmproxy --mode upstream:http://upstream-proxy:8080

# Specifica di una porta specifica
mitmweb --listen-port 9090 --web-port 9091

# Avvio con uno script
mitmproxy -s my_script.py

Sintassi dei filtri di mitmproxy

mitmproxy supporta un potente linguaggio di filtri per selezionare le richieste desiderate:

# ~d — filtro per dominio
~d api.example.com

# ~u — filtro per URL (regex)
~u /api/v2/users

# ~m — filtro per metodo HTTP
~m POST

# ~s — solo risposte
~s ~c 404

# ~c — filtro per codice di stato
~c 500

# Combinazione (AND)
~d api.example.com & ~m POST

# Combinazione (OR)
~c 404 | ~c 500

# NOT
!~d static.example.com

Scrittura di script in Python: intercettazione e modifica del traffico

Gli script sono il principale superpotere di mitmproxy. Con essi puoi modificare automaticamente le richieste e le risposte, registrare i dati nel formato desiderato, simulare errori del server e molto altro. Gli script sono scritti in Python e utilizzano un modello basato su eventi.

Eventi principali (hook)

Hook Quando viene chiamato Oggetto
request Richiesta ricevuta dal client flow.request
response Risposta ricevuta dal server flow.response
error Errore di connessione flow.error
tls_start_client Inizio del handshake TLS con il client tls_start

Esempio 1: registrazione delle richieste in un file

# logger.py
import mitmproxy.http
import json
from datetime import datetime

def request(flow: mitmproxy.http.HTTPFlow) -> None:
    """Registriamo ogni richiesta in un file 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:
    """Registriamo le risposte con codice di errore."""
    if flow.response.status_code >= 400:
        print(f"[ERROR] {flow.request.method} {flow.request.pretty_url} "
              f"-> {flow.response.status_code}")

Avvio dello script:

mitmproxy -s logger.py

Esempio 2: modifica delle richieste — sostituzione delle intestazioni

# modify_headers.py
from mitmproxy import http

def request(flow: http.HTTPFlow) -> None:
    """Sostituiamo User-Agent e aggiungiamo un'intestazione personalizzata."""
    if "api.example.com" in flow.request.pretty_host:
        # Sostituzione di 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"
        )
        # Aggiunta dell'intestazione di autorizzazione
        flow.request.headers["X-Custom-Token"] = "test-token-12345"
        # Rimozione dell'intestazione
        if "X-Debug-Info" in flow.request.headers:
            del flow.request.headers["X-Debug-Info"]

Esempio 3: sostituzione della risposta del server (mock)

# mock_response.py
from mitmproxy import http
import json

def request(flow: http.HTTPFlow) -> None:
    """Intercettiamo la richiesta e restituiamo una risposta mock, senza contattare il server."""
    if flow.request.pretty_url.endswith("/api/v1/user/profile"):
        # Creiamo una risposta mock
        mock_data = {
            "id": 42,
            "name": "Test User",
            "email": "[email protected]",
            "premium": True  # Testiamo la funzionalità premium
        }
        flow.response = http.Response.make(
            200,  # Codice di stato
            json.dumps(mock_data),  # Corpo della risposta
            {"Content-Type": "application/json"}  # Intestazioni
        )

def response(flow: http.HTTPFlow) -> None:
    """Modifichiamo la risposta reale del server."""
    if "/api/v1/products" in flow.request.pretty_url:
        try:
            data = json.loads(flow.response.text)
            # Aggiungiamo un campo a ogni prodotto
            for product in data.get("items", []):
                product["debug_info"] = "intercepted"
            flow.response.text = json.dumps(data)
        except (json.JSONDecodeError, KeyError):
            pass

Esempio 4: simulazione di una connessione lenta e errori

# chaos_testing.py
from mitmproxy import http
import time
import random

def response(flow: http.HTTPFlow) -> None:
    """Chaos engineering: ritardi e errori casuali per il testing."""
    # Aggiungiamo un ritardo casuale da 0 a 2 secondi
    if "api.example.com" in flow.request.pretty_host:
        delay = random.uniform(0, 2.0)
        time.sleep(delay)

    # 10% di probabilità di risposta 503
    if random.random() < 0.1:
        flow.response = http.Response.make(
            503,
            json.dumps({"error": "Service Unavailable"}),
            {"Content-Type": "application/json"}
        )

Intercettazione del traffico delle app mobili

L'analisi del traffico delle app mobili è uno dei compiti più comuni quando si utilizza mitmproxy. È particolarmente utile nel reverse engineering delle API delle app mobili o nel testare la propria app su un dispositivo reale.

Configurazione su Android

Passo 1. Assicurati che il telefono e il computer siano sulla stessa rete Wi-Fi.

Passo 2. Avvia mitmproxy sul computer:

mitmweb --listen-host 0.0.0.0 --listen-port 8080

Passo 3. Su Android: Impostazioni → Wi-Fi → tieni premuto sulla rete → Modifica rete → Avanzate → Proxy → Manuale. Inserisci l'IP del computer e la porta 8080.

Passo 4. Installazione del certificato su Android: apri il browser sul dispositivo, vai su http://mitm.it e scarica il certificato per Android. Poi: Impostazioni → Sicurezza → Installa certificato → Certificato CA.

Android 7+ e Certificate Pinning

A partire da Android 7.0, le app per impostazione predefinita non si fidano dei certificati CA personalizzati. Per intercettare il traffico di tali app sarà necessario avere accesso root o modificare network_security_config.xml nell'APK. Per le app con SSL Pinning, utilizza Frida o Xposed Framework per bypassare il controllo del certificato.

Configurazione su iOS

Passo 1. Configura il proxy in modo simile ad Android: Impostazioni → Wi-Fi → tocca (i) accanto alla rete → Configura proxy → Manuale.

Passo 2. Apri Safari e vai su http://mitm.it — scarica il certificato per iOS.

Passo 3. Installa il profilo: Impostazioni → Profilo caricato → Installa.

Passo 4. Abilita la fiducia nel certificato: Impostazioni → Generali → Info → Fiducia nei certificati — attiva l'interruttore per mitmproxy.

Intercettazione del traffico di un'app specifica tramite Python

# mobile_app_analyzer.py
from mitmproxy import http
import json
import re

# Domini dell'app di interesse
TARGET_DOMAINS = ["api.myapp.com", "cdn.myapp.com"]

def response(flow: http.HTTPFlow) -> None:
    """Analizziamo il traffico dell'app mobile."""
    host = flow.request.pretty_host

    if not any(domain in host for domain in TARGET_DOMAINS):
        return

    # Estraiamo le risposte 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

    # Cerchiamo token nelle intestazioni della richiesta
    auth_header = flow.request.headers.get("authorization", "")
    if auth_header:
        print(f"[AUTH] Token trovato: {auth_header[:50]}...")

Catena di proxy: mitmproxy + upstream proxy

Uno degli scenari potenti è utilizzare mitmproxy in combinazione con un server proxy esterno. Questo consente di intercettare e analizzare il traffico (tramite mitmproxy) e di instradarlo attraverso un indirizzo IP esterno (tramite upstream proxy). Questo schema è utilizzato quando si testano API geolocalizzate o quando si sviluppano applicazioni che devono funzionare tramite proxy.

Modalità upstream proxy

# Inviamo tutto il traffico tramite un upstream HTTP proxy
mitmproxy --mode upstream:http://proxy-host:port

# Upstream SOCKS5 proxy
mitmproxy --mode upstream:socks5://proxy-host:port

# Con autenticazione
mitmproxy --mode upstream:http://user:password@proxy-host:port

# Tramite mitmweb con upstream
mitmweb --mode upstream:http://proxy-host:port

In questa modalità, mitmproxy accetta richieste localmente, decrittografa HTTPS, ti consente di analizzarle e modificarle, e poi le invia tramite un proxy esterno. Questo è particolarmente utile quando si testano API accessibili solo da determinate regioni.

Per tali attività, i proxy residenziali sono molto adatti — hanno indirizzi IP reali di utenti domestici nei paesi desiderati, il che consente di testare correttamente le risposte API geolocalizzate.

Selezione dinamica dell'upstream proxy nello script

# dynamic_upstream.py
from mitmproxy import http
from mitmproxy.net.server_spec import ServerSpec

# Lista di proxy per la rotazione
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:
    """Rotazione dell'upstream proxy per ogni richiesta."""
    global proxy_index

    # Inviamo le richieste all'API tramite proxy diversi
    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"Utilizzando proxy: {proxy_url} per {flow.request.pretty_url}")

Proxy trasparente (modalità trasparente)

In modalità trasparente, l'applicazione non sa che il suo traffico viene intercettato — non è necessario configurare il proxy nelle impostazioni. Questo richiede la configurazione di iptables/pf a livello di sistema operativo:

# Avvio in modalità trasparente
mitmproxy --mode transparent --listen-port 8080

# Configurazione di iptables per reindirizzare il traffico (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

Scenari pratici di utilizzo mitmproxy

Consideriamo compiti specifici che gli sviluppatori risolvono con mitmproxy in progetti reali.

Scenario 1: Testing API senza modificare il server

Immagina: devi verificare come il frontend gestisce una risposta con un array di dati vuoto o un errore di autorizzazione 401, ma riprodurre questo sul server di test è difficile. mitmproxy consente di sostituire la risposta al volo:

# test_edge_cases.py
from mitmproxy import http
import json

def response(flow: http.HTTPFlow) -> None:
    url = flow.request.pretty_url

    # Test: lista di prodotti vuota
    if "/api/products" in url and "test_empty=1" in url:
        flow.response.text = json.dumps({"items": [], "total": 0})

    # Test: token scaduto
    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"}
        )

    # Test: superamento del limite di richieste
    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"}
        )

Scenario 2: Registrazione e riproduzione della sessione

Utile per creare fixture di test o dimostrare funzionalità senza un server reale:

# Registrazione della sessione in un file
mitmdump -w session.dump --filter "~d api.example.com"

# Riproduzione della sessione registrata (offline)
mitmdump -r session.dump

# Conversione in formato HAR per analisi
mitmdump -r session.dump --flow-detail 3 > session.txt

Scenario 3: Documentazione automatica dell'API

# api_documenter.py
from mitmproxy import http
import json
from collections import defaultdict

# Dizionario per accumulare informazioni sugli endpoint
endpoints = defaultdict(lambda: {"methods": set(), "status_codes": set(),
                                  "request_fields": set(), "response_fields": set()})

def _extract_fields(data, prefix=""):
    """Estraiamo ricorsivamente i campi da 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

    # Normalizziamo l'URL (rimuoviamo 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)

    # Estraiamo i campi della richiesta
    if flow.request.text:
        try:
            req_data = json.loads(flow.request.text)
            ep["request_fields"].update(_extract_fields(req_data))
        except json.JSONDecodeError:
            pass

    # Estraiamo i campi della risposta
    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():
    """Stampiamo la documentazione al termine."""
    print("\n=== DOCUMENTAZIONE API ===\n")
    for endpoint, info in sorted(endpoints.items()):
        print(f"Endpoint: {endpoint}")
        print(f"  Codici di stato: {sorted(info['status_codes'])}")
        if info["request_fields"]:
            print(f"  Campi della richiesta: {sorted(info['request_fields'])}")
        if info["response_fields"]:
            print(f"  Campi della risposta: {sorted(info['response_fields'])}")
        print()

Scenario 4: Testing delle risposte geolocalizzate

Quando si sviluppano applicazioni con contenuti regionali, è importante verificare come l'API risponde alle richieste da diversi paesi. Per questo, mitmproxy viene eseguito in modalità upstream con proxy di data center dai paesi desiderati — questo è un modo veloce e affidabile per simulare richieste da paesi specifici.

# geo_test.py
from mitmproxy import http

def response(flow: http.HTTPFlow) -> None:
    """Registriamo intestazioni e dati geolocalizzati."""
    # Controlliamo quale contenuto restituisce il server
    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}")

    # Cerchiamo menzioni di valute e localizzazioni nella risposta
    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}")

Scenario 5: Utilizzo di mitmproxy in CI/CD

mitmdump è perfetto per i test di integrazione nel pipeline CI/CD — viene eseguito come processo in background, registra il traffico e termina insieme ai test:

#!/bin/bash
# ci_test.sh

# Avviamo mitmdump in background
mitmdump -w test_traffic.dump -s ci_assertions.py &
MITM_PID=$!

# Diamo tempo al proxy di avviarsi
sleep 1

# Avviamo i test con il proxy
export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080
pytest tests/integration/ -v

# Fermiamo mitmdump
kill $MITM_PID

# Analizziamo il traffico registrato
mitmdump -r test_traffic.dump -s analyze_traffic.py

Lo script ci_assertions.py può verificare che l'applicazione non faccia richieste superflue, non trasmetta dati sensibili in chiaro e rispetti i contratti API.

Scenario 6: Analisi del traffico del parser

Durante lo sviluppo di parser, mitmproxy aiuta a capire quali richieste fa il browser durante il caricamento della pagina — comprese le richieste XHR/fetch all'API, che non sono visibili nel codice sorgente HTML. Questo consente di accedere direttamente all'API del sito anziché analizzare l'HTML. Durante lo sviluppo di tali soluzioni, si utilizzano spesso proxy residenziali per la rotazione degli IP, per evitare blocchi durante la raccolta dei dati.

Conclusione

mitmproxy è uno degli strumenti più potenti nell'arsenale di uno sviluppatore per lavorare con il traffico HTTP/HTTPS. Combina le funzioni di debugger, ambiente di test, documentatore API e strumento per l'analisi della sicurezza. Le tre interfacce — console, web e CLI — coprono tutti gli scenari: dal debugging interattivo all'automazione in CI/CD.

```