mitmproxy 完全指南

最后更新:2026-03-31 相关文档:js逆向调试技巧(/js-ni-xiang-diao-shi-ji-qiao/) Frida基础(/frida-ji-chu-zhi-nan/) 反反爬技术汇总(/fan-fan-pa-ji-zhu-hui-zong/) mitmproxy 是一个开源的中间人代理工具,支持拦截、修改、重放 HTTP/HTTPS 流量。 mitmproxy 通过 Python 插件实现自动化处理。 只处理目标域名,减少无关噪声: 未安装证书时,HTTPS 请求会显示为 << Killed 或 SSL Error,需在目标设备安装并

分享

官方文档:https://docs.mitmproxy.org/
适用版本:mitmproxy 10.x+(2026-05-08 核实)

最后更新:2026-03-31

相关文档:js逆向调试技巧 Frida基础 反反爬技术汇总


1. 基础概念

mitmproxy 是什么

mitmproxy 是一个开源的中间人代理工具,支持拦截、修改、重放 HTTP/HTTPS 流量。

组件 说明
mitmproxy 交互式终端界面(TUI)
mitmweb Web 界面,可视化操作
mitmdump 命令行脚本模式,可编写插件

安装

pip install mitmproxy

# 或下载预编译二进制
# https://mitmproxy.org/downloads/

证书安装(HTTPS 抓包前提)

# 1. 启动 mitmproxy
mitmproxy

# 2. 设备配置代理指向 mitmproxy(默认端口 8080)
# 3. 访问 http://mitm.it 下载证书
# 4. 安装证书为受信任根证书

# Android 证书安装(需 root)
adb push ~/.mitmproxy/mitmproxy-ca-cert.pem /sdcard/
# 在手机上:设置 → 安全 → 从存储设备安装证书

# iOS 证书安装
# 访问 http://mitm.it,下载 iOS 证书并信任

2. 基础使用

启动方式

# 普通代理模式
mitmproxy -p 8080

# Web 界面模式
mitmweb -p 8080

# 脚本模式
mitmdump -s script.py -p 8080

# 透明代理模式(不修改客户端配置,需 iptables 配合)
mitmproxy --mode transparent -p 8080

# 上游代理(mitmproxy → 上游代理 → 目标)
mitmproxy --mode upstream:http://proxy:8888

# SOCKS5 代理模式
mitmproxy --mode socks5 -p 1080

mitmproxy TUI 快捷键

按键 功能
Enter 查看请求详情
q 返回上一级
f 设置过滤器
i 设置拦截规则
a 放行拦截的请求
r 重放请求
e 编辑请求/响应
d 删除条目
z 清空列表
w 保存流量到文件

过滤语法

# 过滤域名
~d example.com

# 过滤 URL 包含关键词
~u api/sign

# 过滤请求方法
~m POST

# 过滤响应状态码
~s 200

# 组合条件
~d example.com & ~u api

# 排除静态资源
~d example.com & !~u \.(js|css|png|jpg|gif)$

3. 插件开发(核心用法)

mitmproxy 通过 Python 插件实现自动化处理。

插件基础结构

# script.py
from mitmproxy import http, ctx

class MyAddon:
    def request(self, flow: http.HTTPFlow) -> None:
        """拦截请求"""
        pass

    def response(self, flow: http.HTTPFlow) -> None:
        """拦截响应"""
        pass

addons = [MyAddon()]

打印所有请求

from mitmproxy import http

class Logger:
    def request(self, flow: http.HTTPFlow) -> None:
        print(f">> {flow.request.method} {flow.request.pretty_url}")
        if flow.request.content:
            print(f"   Body: {flow.request.content[:200]}")

    def response(self, flow: http.HTTPFlow) -> None:
        print(f"<< {flow.response.status_code} {flow.request.pretty_url}")

addons = [Logger()]

捕获 API 响应(提取加密参数)

import json
from mitmproxy import http

class ApiCapture:
    def response(self, flow: http.HTTPFlow) -> None:
        # 只处理目标 API
        if "api.example.com" not in flow.request.host:
            return
        if "/sign" not in flow.request.path:
            return

        # 获取请求头(找 sign 参数位置)
        headers = dict(flow.request.headers)
        print("Request Headers:", json.dumps(headers, indent=2))

        # 获取请求体
        try:
            body = json.loads(flow.request.content)
            print("Request Body:", json.dumps(body, indent=2, ensure_ascii=False))
        except Exception:
            print("Request Body (raw):", flow.request.content)

        # 获取响应体
        try:
            resp = json.loads(flow.response.content)
            print("Response:", json.dumps(resp, indent=2, ensure_ascii=False))
        except Exception:
            pass

addons = [ApiCapture()]

修改请求

from mitmproxy import http

class RequestModifier:
    def request(self, flow: http.HTTPFlow) -> None:
        # 添加/修改请求头
        flow.request.headers["X-Custom-Header"] = "my-value"
        flow.request.headers["Authorization"] = "Bearer fake-token"

        # 修改请求参数
        if flow.request.path.startswith("/api/search"):
            flow.request.query["page"] = "1"

        # 替换请求体(JSON)
        if "application/json" in flow.request.headers.get("content-type", ""):
            import json
            body = json.loads(flow.request.content)
            body["modified"] = True
            flow.request.content = json.dumps(body).encode()

addons = [RequestModifier()]

修改响应(Mock 接口)

import json
from mitmproxy import http

class ResponseMocker:
    def response(self, flow: http.HTTPFlow) -> None:
        if "/api/list" in flow.request.path:
            # 替换响应体
            mock_data = {"code": 0, "data": [], "total": 0}
            flow.response.content = json.dumps(mock_data).encode()
            flow.response.headers["content-type"] = "application/json"
            flow.response.status_code = 200

addons = [ResponseMocker()]

重定向请求(本地替换)

from mitmproxy import http

class LocalReplace:
    def request(self, flow: http.HTTPFlow) -> None:
        # 将线上 JS 替换为本地修改版
        if flow.request.path.endswith("/encrypt.js"):
            with open("/tmp/encrypt_patched.js", "rb") as f:
                flow.response = http.Response.make(
                    200,
                    f.read(),
                    {"Content-Type": "application/javascript"},
                )

addons = [LocalReplace()]

4. 流量保存与重放

# 保存流量
mitmdump -w traffic.mitm

# 从文件重放
mitmdump -n -r traffic.mitm -s process.py

# 过滤保存
mitmdump -w traffic.mitm "~d target.com & ~m POST"
# 从保存文件读取并分析
from mitmproxy.io import FlowReader

with open("traffic.mitm", "rb") as f:
    reader = FlowReader(f)
    for flow in reader.stream():
        print(flow.request.pretty_url)
        print(flow.response.status_code)

5. 绕过证书检测

Android SSL Pinning 绕过

# 配合 Frida 使用
# 参见 Frida基础.md 中的 SSL Pinning 绕过章节

# 或使用 apk-mitm 工具自动修改 APK 的 network security config
# pip install apk-mitm
# apk-mitm target.apk

忽略特定证书错误

# 允许所有不安全的证书(仅测试用)
mitmproxy --ssl-insecure

处理双向证书认证(Mutual TLS)

# mitmproxy 支持客户端证书
mitmproxy --client-certs /path/to/client.pem

6. 实战场景

场景 1:自动提取 sign 参数并用 Python 重放

# extract_sign.py
import json
import threading
from mitmproxy import http

captured_requests = []

class SignExtractor:
    def request(self, flow: http.HTTPFlow) -> None:
        if "api.example.com" not in flow.request.host:
            return

        captured_requests.append({
            "url": flow.request.pretty_url,
            "headers": dict(flow.request.headers),
            "body": flow.request.get_text(),
            "timestamp": flow.request.timestamp_start,
        })

addons = [SignExtractor()]

场景 2:批量修改请求参数

import json
from mitmproxy import http

class BatchModifier:
    """自动翻页采集:修改 page 参数"""
    def __init__(self):
        self.current_page = 1

    def request(self, flow: http.HTTPFlow) -> None:
        if "/api/list" in flow.request.path:
            if "application/json" in flow.request.headers.get("content-type", ""):
                body = json.loads(flow.request.content)
                body["page"] = self.current_page
                flow.request.content = json.dumps(body).encode()

    def response(self, flow: http.HTTPFlow) -> None:
        if "/api/list" in flow.request.path:
            data = json.loads(flow.response.content)
            # 保存数据...
            self.current_page += 1

addons = [BatchModifier()]

7. 最佳实践

精确过滤目标流量

只处理目标域名,减少无关噪声:

TARGET_HOST = "api.example.com"

def request(self, flow: http.HTTPFlow) -> None:
    if flow.request.host != TARGET_HOST:
        return
    # 处理逻辑

使用 ctx.log 替代 print

from mitmproxy import ctx

ctx.log.info("处理请求")
ctx.log.warn("发现异常")
ctx.log.error("严重错误")

8. 踩坑与注意事项

HTTPS 需要安装证书

未安装证书时,HTTPS 请求会显示为 << KilledSSL Error,需在目标设备安装并信任 mitmproxy 根证书。

响应乱码问题

有些响应是 gzip 压缩的,mitmproxy 自动解压,但手动读 flow.response.raw_content 会得到压缩数据,应使用 flow.response.content(自动解压)。

HTTP/2 兼容性

部分服务使用 HTTP/2,可禁用降级到 HTTP/1.1:

mitmproxy --no-http2

脚本热重载

修改插件脚本后不需要重启:

# mitmdump 支持脚本热重载
mitmdump -s script.py
# 在 mitmproxy TUI 中:P → 编辑脚本路径 → 重新加载

最佳实践

mitmdump 替代 mitmproxy 做批量处理mitmproxy 需要交互终端,不适合自动化;mitmdump -s script.py 可在无头服务器上运行,脚本处理所有流量后直接输出,适合 CI/长期挂载场景。

addon 脚本用 response 钩子而非 request:修改响应内容(如注入 JS、解密响应体)在 response 钩子中处理;修改请求头或签名参数在 request 钩子中;两个钩子都可以访问完整的请求和响应数据。

证书信任一次性完成,不要反复安装:在目标设备安装 mitmproxy CA 证书后,该证书对所有 HTTPS 流量生效;若 SSL Pinning 导致证书被拒,需要配合 Frida 或 objection 绕过固定证书校验。

用过滤表达式减少噪音mitmproxy -s script.py --filter '~url api.target.com' 只处理匹配 URL 的流量,避免广告/CDN 流量干扰分析;过滤表达式支持正则、域名、方法、状态码等维度。

脚本里读取请求体时注意编码flow.request.content 返回 bytes,若请求体是 JSON 用 flow.request.json() 直接解析;若是 URL-encoded 用 flow.request.urlencoded_form 读取表单字段。


常见陷阱

陷阱:安装证书后 HTTPS 仍然失败

现象: 已在设备信任 mitmproxy CA,但 HTTPS 请求仍报证书错误。
原因: App 使用了 SSL Pinning,将服务端证书或公钥硬编码到 App 中,即使系统信任 mitmproxy 证书也会被拒绝。
解决: Android 用 Frida + ssl-kill-switch2,iOS 用 SSL Kill Switch 3 绕过 Pinning;或 hook App 的证书验证函数让其始终返回成功。

陷阱:修改响应后 App 崩溃或显示异常

现象:response 钩子中修改了响应体,但 App 端崩溃或请求被标记为失败。
原因: 修改了响应体后 Content-Length 头还是旧值,导致 App 读取截断;或响应体被签名/校验,修改后校验失败。
解决: 修改 flow.response.content 后,mitmproxy 会自动更新 Content-Length(调用 set_content);若 App 校验响应签名,还需同时修改签名字段。

陷阱:mitmdump 脚本异常不显示错误信息

现象: 脚本运行但逻辑没有按预期执行,也没有报错。
原因: mitmdump 默认吞掉 addon 中未捕获的异常,异常信息只打印到 stderr。
解决: 在脚本中用 try/except 包裹并 print(traceback.format_exc()) 输出;或加 -v 参数提高日志详细程度:mitmdump -v -s script.py


参见

Frida基础
js逆向调试技巧
反反爬技术汇总

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog