Está depurando um aplicativo móvel e não entende quais solicitações ele envia ao servidor? Precisa testar o comportamento da API sob diferentes condições ou interceptar a resposta e modificar os dados em tempo real? O mitmproxy resolve todas essas tarefas — é uma ferramenta gratuita e de código aberto que permite controlar completamente o tráfego HTTP e HTTPS entre o cliente e o servidor.
O que é mitmproxy e por que é necessário para o desenvolvedor
O mitmproxy é um proxy MITM (Man-In-The-Middle) interativo de código aberto, escrito em Python. Ele funciona como um intermediário entre seu aplicativo e o servidor: intercepta todas as solicitações e respostas, permitindo visualizá-las, modificá-las, reproduzi-las e salvá-las.
A principal diferença do mitmproxy em relação a proxies comuns é a capacidade de trabalhar com tráfego HTTPS criptografado. A ferramenta gera dinamicamente certificados SSL para cada domínio, permitindo a descriptografia do tráfego em tempo real, sem interromper o funcionamento do aplicativo.
Aqui estão algumas tarefas típicas que os desenvolvedores resolvem com o mitmproxy:
- Depuração de API — você vê as solicitações e respostas exatas, incluindo cabeçalhos, corpo e códigos de status.
- Engenharia reversa — analisa como aplicativos e serviços de terceiros funcionam.
- Teste — substitui as respostas do servidor para verificar casos limite.
- Automatização — escreve scripts para modificar o tráfego com base em condições.
- Gravação e reprodução — salva a sessão e a reproduz sem um servidor real.
- Análise de segurança — verifica se o aplicativo não está transmitindo dados desnecessários.
É importante entender
O mitmproxy é uma ferramenta para testes e desenvolvimento legais. Use-o apenas para analisar o tráfego de aplicativos que você desenvolve ou tem permissão para testar. Interceptar tráfego de terceiros sem permissão viola a legislação.
A ferramenta é fornecida em três variantes: interface interativa de console mitmproxy, interface web mitmweb e utilitário de linha de comando mitmdump. Todas as três usam um núcleo comum e suportam scripts em Python.
Instalação do mitmproxy no Windows, macOS e Linux
O mitmproxy pode ser instalado de várias maneiras. Recomendamos usar pip — isso garante a versão mais recente e uma atualização simples.
Instalação via pip (método universal)
É necessário Python 3.9 ou superior. Verifique a versão do Python:
python --version
# ou
python3 --version
Instalando o mitmproxy:
pip install mitmproxy
Após a instalação, verifique:
mitmproxy --version
# Deve exibir: mitmproxy 10.x.x
Instalação via gerenciadores de pacotes
macOS (Homebrew):
brew install mitmproxy
Linux (Ubuntu/Debian):
sudo apt install mitmproxy
# ou via snap para a versão mais recente:
sudo snap install mitmproxy
Windows: Baixe o instalador do site oficial mitmproxy.org ou use pip no PowerShell com privilégios de administrador.
Execução e verificação básica
Por padrão, o mitmproxy escuta na porta 8080. Inicie a interface web para começar:
# Iniciando a interface web na porta 8080
mitmweb
# Iniciando em outra porta
mitmweb --listen-port 9090
# Interface de console
mitmproxy
Após iniciar mitmweb, abra o navegador no endereço http://127.0.0.1:8081 — esta é a interface web para visualização do tráfego. O próprio proxy funciona na porta 8080.
Configuração de certificados SSL para interceptação de HTTPS
A interceptação de tráfego HTTPS requer a instalação do certificado raiz do mitmproxy no sistema ou navegador. Sem essa etapa, o navegador mostrará um aviso sobre a conexão insegura, e muitos aplicativos podem se recusar a funcionar.
Como isso funciona tecnicamente
Na primeira execução, o mitmproxy cria automaticamente um certificado CA raiz e o salva no diretório ~/.mitmproxy/. Quando um cliente se conecta a um site HTTPS através do proxy, o mitmproxy gera em tempo real um certificado para esse domínio, assinando-o com sua CA. O cliente confia nesse certificado se a CA estiver adicionada às confiáveis — e a descriptografia ocorre de forma transparente.
Instalação do certificado no sistema
Os certificados estão localizados em ~/.mitmproxy/:
mitmproxy-ca-cert.pem— para Linux/macOSmitmproxy-ca-cert.cer— para Windowsmitmproxy-ca-cert.p12— para 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: Clique duas vezes no arquivo mitmproxy-ca-cert.cer → "Instalar certificado" → "Computador local" → "Autoridades de certificação raiz confiáveis".
Instalação no navegador Firefox
O Firefox usa seu próprio armazenamento de certificados. Vá para: Configurações → Privacidade e segurança → Certificados → Ver certificados → Autoridades de certificação → Importar. Selecione o arquivo mitmproxy-ca-cert.pem e marque "Confiar na identificação de sites".
Verificação do funcionamento
Configure o navegador para usar o proxy 127.0.0.1:8080 e abra qualquer site HTTPS. Na interface do mitmweb, você deve ver o tráfego descriptografado. Alternativamente — abra http://mitm.it através do proxy configurado: o mitmproxy mostrará instruções para instalar o certificado para sua plataforma.
Três interfaces: mitmproxy, mitmweb e mitmdump
O pacote mitmproxy inclui três utilitários com diferentes interfaces para diferentes cenários de trabalho. Compreender as diferenças ajudará a escolher a ferramenta certa para cada tarefa.
| Utilitário | Interface | Quando usar | Características |
|---|---|---|---|
mitmproxy |
TUI de console | Depuração interativa no terminal | Requer terminal com suporte a cores, poderoso filtro |
mitmweb |
Navegador web | Análise visual do tráfego | UI amigável, suporte a filtros, exportação |
mitmdump |
CLI (stdout) | Scripts, CI/CD, automação | Sem interatividade, saída para arquivo ou pipe |
Flags úteis de execução
# Gravar tráfego em um arquivo
mitmdump -w traffic.dump
# Reproduzir tráfego gravado
mitmdump -r traffic.dump
# Filtragem: apenas solicitações para um domínio específico
mitmproxy --filter "~d api.example.com"
# Executar no modo proxy transparente
mitmproxy --mode transparent
# Executar como proxy upstream (cadeia de proxy)
mitmproxy --mode upstream:http://upstream-proxy:8080
# Especificando uma porta específica
mitmweb --listen-port 9090 --web-port 9091
# Executar com um script
mitmproxy -s my_script.py
Sintaxe de filtros do mitmproxy
O mitmproxy suporta uma poderosa linguagem de filtros para selecionar as solicitações desejadas:
# ~d — filtro por domínio
~d api.example.com
# ~u — filtro por URL (regex)
~u /api/v2/users
# ~m — filtro por método HTTP
~m POST
# ~s — apenas respostas
~s ~c 404
# ~c — filtro por código de status
~c 500
# Combinação (E)
~d api.example.com & ~m POST
# Combinação (OU)
~c 404 | ~c 500
# NÃO
!~d static.example.com
Escrita de scripts em Python: interceptação e modificação de tráfego
Scripts são o principal superpoder do mitmproxy. Com eles, você pode modificar automaticamente solicitações e respostas, registrar dados no formato desejado, simular erros de servidor e muito mais. Os scripts são escritos em Python e utilizam um modelo baseado em eventos.
Eventos principais (hooks)
| Hook | Quando é chamado | Objeto |
|---|---|---|
request |
Recebida uma solicitação do cliente | flow.request |
response |
Recebida uma resposta do servidor | flow.response |
error |
Erro de conexão | flow.error |
tls_start_client |
Início do handshake TLS com o cliente | tls_start |
Exemplo 1: registrando solicitações em um arquivo
# logger.py
import mitmproxy.http
import json
from datetime import datetime
def request(flow: mitmproxy.http.HTTPFlow) -> None:
"""Registramos cada solicitação em um arquivo 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:
"""Registramos respostas com código de erro."""
if flow.response.status_code >= 400:
print(f"[ERROR] {flow.request.method} {flow.request.pretty_url} "
f"-> {flow.response.status_code}")
Executando o script:
mitmproxy -s logger.py
Exemplo 2: modificação de solicitações — substituição de cabeçalhos
# modify_headers.py
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
"""Substituímos o User-Agent e adicionamos um cabeçalho personalizado."""
if "api.example.com" in flow.request.pretty_host:
# Substituição do 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"
)
# Adicionando cabeçalho de autorização
flow.request.headers["X-Custom-Token"] = "test-token-12345"
# Removendo cabeçalho
if "X-Debug-Info" in flow.request.headers:
del flow.request.headers["X-Debug-Info"]
Exemplo 3: substituição da resposta do servidor (mock)
# mock_response.py
from mitmproxy import http
import json
def request(flow: http.HTTPFlow) -> None:
"""Interceptamos a solicitação e retornamos uma resposta mock, sem acessar o servidor."""
if flow.request.pretty_url.endswith("/api/v1/user/profile"):
# Criamos uma resposta mock
mock_data = {
"id": 42,
"name": "Test User",
"email": "[email protected]",
"premium": True # Testando funcionalidade premium
}
flow.response = http.Response.make(
200, # Código de status
json.dumps(mock_data), # Corpo da resposta
{"Content-Type": "application/json"} # Cabeçalhos
)
def response(flow: http.HTTPFlow) -> None:
"""Modificamos a resposta real do servidor."""
if "/api/v1/products" in flow.request.pretty_url:
try:
data = json.loads(flow.response.text)
# Adicionamos um campo a cada produto
for product in data.get("items", []):
product["debug_info"] = "intercepted"
flow.response.text = json.dumps(data)
except (json.JSONDecodeError, KeyError):
pass
Exemplo 4: simulação de conexão lenta e erros
# chaos_testing.py
from mitmproxy import http
import time
import random
def response(flow: http.HTTPFlow) -> None:
"""Engenharia do caos: atrasos e erros aleatórios para teste."""
# Adicionamos um atraso aleatório de 0 a 2 segundos
if "api.example.com" in flow.request.pretty_host:
delay = random.uniform(0, 2.0)
time.sleep(delay)
# 10% de chance de resposta 503
if random.random() < 0.1:
flow.response = http.Response.make(
503,
json.dumps({"error": "Service Unavailable"}),
{"Content-Type": "application/json"}
)
Interceptação de tráfego de aplicativos móveis
A análise de tráfego de aplicativos móveis é uma das tarefas mais comuns ao usar o mitmproxy. Isso é especialmente útil ao realizar engenharia reversa de APIs de aplicativos móveis ou testar seu próprio aplicativo em um dispositivo real.
Configuração no Android
Passo 1. Certifique-se de que o telefone e o computador estão na mesma rede Wi-Fi.
Passo 2. Inicie o mitmproxy no computador:
mitmweb --listen-host 0.0.0.0 --listen-port 8080
Passo 3. No Android: Configurações → Wi-Fi → mantenha pressionada a rede → Alterar rede → Avançado → Proxy → Manual. Insira o IP do computador e a porta 8080.
Passo 4. Instalação do certificado no Android: abra o navegador no dispositivo, vá para http://mitm.it e baixe o certificado para Android. Em seguida: Configurações → Segurança → Instalar certificado → Certificado CA.
Android 7+ e Pinagem de Certificado
A partir do Android 7.0, aplicativos não confiam por padrão em certificados CA personalizados. Para interceptar o tráfego de tais aplicativos, será necessário acesso root ou modificação do network_security_config.xml no APK. Para aplicativos com SSL Pinning, use Frida ou Xposed Framework para contornar a verificação do certificado.
Configuração no iOS
Passo 1. Configure o proxy da mesma forma que no Android: Configurações → Wi-Fi → toque em (i) ao lado da rede → Configurar proxy → Manual.
Passo 2. Abra o Safari e vá para http://mitm.it — baixe o certificado para iOS.
Passo 3. Instale o perfil: Configurações → Perfil baixado → Instalar.
Passo 4. Ative a confiança no certificado: Configurações → Geral → Sobre → Confiar em certificados — ative o interruptor para mitmproxy.
Interceptação de tráfego de um aplicativo específico via Python
# mobile_app_analyzer.py
from mitmproxy import http
import json
import re
# Domínios do aplicativo de interesse
TARGET_DOMAINS = ["api.myapp.com", "cdn.myapp.com"]
def response(flow: http.HTTPFlow) -> None:
"""Analisamos o tráfego do aplicativo móvel."""
host = flow.request.pretty_host
if not any(domain in host for domain in TARGET_DOMAINS):
return
# Extraímos respostas 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
# Procuramos tokens nos cabeçalhos da solicitação
auth_header = flow.request.headers.get("authorization", "")
if auth_header:
print(f"[AUTH] Token encontrado: {auth_header[:50]}...")
Cadeia de proxy: mitmproxy + proxy upstream
Um dos cenários poderosos é usar o mitmproxy em conjunto com um servidor proxy externo. Isso permite interceptar e analisar o tráfego (via mitmproxy) e direcioná-lo através de um endereço IP externo (via proxy upstream). Esse esquema é usado ao testar APIs geograficamente dependentes ou ao desenvolver aplicativos que devem funcionar através de um proxy.
Modo proxy upstream
# Direcionando todo o tráfego através de um proxy HTTP upstream
mitmproxy --mode upstream:http://proxy-host:port
# Proxy SOCKS5 upstream
mitmproxy --mode upstream:socks5://proxy-host:port
# Com autenticação
mitmproxy --mode upstream:http://user:password@proxy-host:port
# Através do mitmweb com upstream
mitmweb --mode upstream:http://proxy-host:port
Nesse modo, o mitmproxy aceita solicitações localmente, descriptografa o HTTPS, permite que você as analise e modifique, e então as envia através do proxy externo. Isso é especialmente conveniente ao testar APIs disponíveis apenas de determinadas regiões.
Para essas tarefas, proxies residenciais são uma boa opção — eles têm endereços IP reais de usuários domésticos dos países desejados, permitindo testar corretamente as respostas geograficamente dependentes da API.
Seleção dinâmica de proxy upstream em script
# dynamic_upstream.py
from mitmproxy import http
from mitmproxy.net.server_spec import ServerSpec
# Lista de proxies para rotação
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:
"""Rotação de proxies upstream para cada solicitação."""
global proxy_index
# Direcionando solicitações para a API através de diferentes proxies
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"Usando proxy: {proxy_url} para {flow.request.pretty_url}")
Proxy transparente (modo transparente)
No modo transparente, o aplicativo não sabe que seu tráfego está sendo interceptado — não é necessário configurar o proxy nas configurações. Isso requer configuração do iptables/pf no nível do sistema operacional:
# Executando no modo transparente
mitmproxy --mode transparent --listen-port 8080
# Configurando iptables para redirecionar tráfego (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
Cenários práticos de aplicação mitmproxy
Vamos considerar tarefas específicas que os desenvolvedores resolvem com o mitmproxy em projetos reais.
Cenário 1: Testando a API sem alterar o servidor
Imagine: você precisa verificar como o frontend lida com uma resposta com um array de dados vazio ou um erro de autorização 401, mas reproduzir isso em um servidor de teste é difícil. O mitmproxy permite substituir a resposta em tempo real:
# test_edge_cases.py
from mitmproxy import http
import json
def response(flow: http.HTTPFlow) -> None:
url = flow.request.pretty_url
# Teste: lista de produtos vazia
if "/api/products" in url and "test_empty=1" in url:
flow.response.text = json.dumps({"items": [], "total": 0})
# Teste: token expirado
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"}
)
# Teste: limite de solicitações excedido
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"}
)
Cenário 2: Gravação e reprodução de sessão
Útil para criar fixtures de teste ou demonstrar funcionalidades sem um servidor real:
# Gravar sessão em um arquivo
mitmdump -w session.dump --filter "~d api.example.com"
# Reproduzir sessão gravada (offline)
mitmdump -r session.dump
# Converter para formato HAR para análise
mitmdump -r session.dump --flow-detail 3 > session.txt
Cenário 3: Documentação automática da API
# api_documenter.py
from mitmproxy import http
import json
from collections import defaultdict
# Dicionário para acumular informações sobre os endpoints
endpoints = defaultdict(lambda: {"methods": set(), "status_codes": set(),
"request_fields": set(), "response_fields": set()})
def _extract_fields(data, prefix=""):
"""Extraímos campos de JSON recursivamente."""
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
# Normalizamos a URL (removendo 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)
# Extraímos campos da solicitação
if flow.request.text:
try:
req_data = json.loads(flow.request.text)
ep["request_fields"].update(_extract_fields(req_data))
except json.JSONDecodeError:
pass
# Extraímos campos da resposta
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():
"""Exibimos a documentação ao finalizar."""
print("\n=== DOCUMENTAÇÃO DA API ===\n")
for endpoint, info in sorted(endpoints.items()):
print(f"Endpoint: {endpoint}")
print(f" Códigos de status: {sorted(info['status_codes'])}")
if info["request_fields"]:
print(f" Campos da solicitação: {sorted(info['request_fields'])}")
if info["response_fields"]:
print(f" Campos da resposta: {sorted(info['response_fields'])}")
print()
Cenário 4: Testando respostas geograficamente dependentes
Ao desenvolver aplicativos com conteúdo regional, é importante verificar como a API responde a solicitações de diferentes países. Para isso, o mitmproxy é executado no modo upstream com proxies de data center das regiões desejadas — é uma maneira rápida e confiável de simular solicitações de países específicos.
# geo_test.py
from mitmproxy import http
def response(flow: http.HTTPFlow) -> None:
"""Registramos cabeçalhos e dados geograficamente dependentes."""
# Verificamos qual conteúdo o servidor retorna
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}")
# Procuramos menções a moedas e locais na resposta
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}")
Cenário 5: Usando mitmproxy em CI/CD
O mitmdump é ideal para testes de integração em um pipeline de CI/CD — ele é executado como um processo em segundo plano, grava o tráfego e termina junto com os testes:
#!/bin/bash
# ci_test.sh
# Iniciando mitmdump em segundo plano
mitmdump -w test_traffic.dump -s ci_assertions.py &
MITM_PID=$!
# Dando tempo para o proxy iniciar
sleep 1
# Executando testes com o proxy
export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080
pytest tests/integration/ -v
# Parando o mitmdump
kill $MITM_PID
# Analisando o tráfego gravado
mitmdump -r test_traffic.dump -s analyze_traffic.py
O script ci_assertions.py pode verificar se o aplicativo não está fazendo solicitações desnecessárias, não está transmitindo dados sensíveis em texto não criptografado e está seguindo os contratos da API.
Cenário 6: Análise de tráfego de parser
Ao desenvolver parsers, o mitmproxy ajuda a entender quais solicitações o navegador faz ao carregar uma página — incluindo solicitações XHR/fetch para a API que não são visíveis no código-fonte HTML. Isso permite que você acesse diretamente a API do site em vez de fazer parsing do HTML. Ao desenvolver tais soluções, proxies residenciais são frequentemente usados para rotação de IP, a fim de evitar bloqueios ao coletar dados.
Conclusão
O mitmproxy é uma das ferramentas mais poderosas no arsenal de um desenvolvedor para trabalhar com tráfego HTTP/HTTPS. Ele combina funções de depurador, ambiente de teste, documentador de API e ferramenta de análise de segurança. As três interfaces — console, web e CLI — cobrem todos os cenários: desde depuração interativa até automação em CI/CD.
```