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/macOSmitmproxy-ca-cert.cer— per Windowsmitmproxy-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.
```