3、mitmproxy基础入门:启动模式、流量拦截原理与第一个录制脚本

好,咱们正式开始动手了。

mitmproxy 这工具,说白了就是一个中间人代理。你想想看,手机或浏览器发的请求,先经过它,它帮你转发给服务器。服务器返回的数据,也先经过它,再回到客户端。这样一来,所有流量都能被它“看”到,甚至“改”掉。

我在项目中遇到过不少测试同学,一上来就问我:“怎么抓 HTTPS 的包?”其实 mitmproxy 就是干这个的。而且它比 Fiddler 或 Charles 更轻量,更适合脚本化、自动化。

3.1 三种启动模式:mitmproxy / mitmweb / mitmdump

mitmproxy 提供了三种启动方式。我个人的习惯是:调试用 mitmweb,跑自动化用 mitmdump,偶尔想装酷就用 mitmproxy 终端界面。

模式 命令 特点 我什么时候用
mitmproxy mitmproxy 终端交互界面,支持键盘操作 服务器上调试,没图形界面时
mitmweb mitmweb Web 图形界面,浏览器打开即可 本地开发,可视化查看流量
mitmdump mitmdump 纯命令行,无界面,适合脚本 自动化录制、回放、持续集成
小提示: 如果你刚接触,我建议先用 mitmweb。因为它最直观,能看到请求列表、响应内容,还能直接搜索过滤。等你熟悉了,再切到 mitmdump 写脚本。

3.2 流量拦截原理

原理其实不复杂。我画了一张图,帮你理解整个流程:

客户端 (浏览器/App) mitmproxy 中间人代理 (拦截/修改/记录) 目标服务器 请求 转发请求 返回响应 转发响应 流量拦截原理:所有数据都经过 mitmproxy 客户端 -> mitmproxy -> 服务器 -> mitmproxy -> 客户端

看到没?客户端和服务器之间,插了一个“中间人”。这个中间人就是 mitmproxy。它拿到请求后,你可以选择:

  • 直接放行:啥也不改,原样转发
  • 修改请求:改 Header、改 Body、改 URL
  • 拦截响应:改返回数据,模拟各种场景
  • 记录流量:存下来,后面回放用
注意: 抓 HTTPS 流量时,需要在客户端安装 mitmproxy 的 CA 证书。否则客户端会报“证书错误”或“连接不安全”。我曾经在第一次配置时忘了这步,折腾了半小时才发现是证书没装。

3.3 第一个录制脚本:Hello World

好了,理论说完了,咱们写个真正的脚本。

这个脚本的功能很简单:拦截所有请求,打印出请求的 URL 和响应状态码。说白了就是“Hello World”级别的入门脚本。

3.3.1 创建脚本文件

新建一个文件,叫 hello_world.py:

# hello_world.py
# 我的第一个 mitmproxy 录制脚本

from mitmproxy import http

def request(flow: http.HTTPFlow):
    """
    每当有请求经过时,这个函数会被自动调用。
    """
    url = flow.request.pretty_url
    method = flow.request.method
    print(f"[请求] {method} {url}")

def response(flow: http.HTTPFlow):
    """
    每当有响应返回时,这个函数会被自动调用。
    """
    url = flow.request.pretty_url
    status = flow.response.status_code
    print(f"[响应] {url} -> {status}")

代码很简单,就两个函数:request 和 response。mitmproxy 会自动识别它们,并在对应时机调用。

核心要点: mitmproxy 的脚本机制就是“钩子函数”。你定义好函数名(比如 request、response、error),它就会在对应事件触发时调用你写的逻辑。不需要你手动去“启动”什么循环。

3.3.2 运行脚本

用 mitmdump 模式运行:

mitmdump -s hello_world.py

然后打开浏览器,访问任意网站(比如 http://example.com)。你会看到终端里不断打印出请求和响应的信息:

[请求] GET http://example.com/
[响应] http://example.com/ -> 200
[请求] GET http://example.com/favicon.ico
[响应] http://example.com/favicon.ico -> 404

嗯,到这里,你的第一个录制脚本就跑起来了。

3.3.3 保存流量到文件

光打印还不够,咱们得把流量存下来,后面才能回放。修改一下脚本:

# hello_world.py - 带保存功能

from mitmproxy import http
from mitmproxy import io
import time

# 用于存储所有流量的列表
flows = []

def request(flow: http.HTTPFlow):
    flows.append(flow)

def response(flow: http.HTTPFlow):
    # 这里可以加一些过滤逻辑,比如只保存 200 的响应
    pass

def done():
    """
    当 mitmproxy 退出时,这个函数会被调用。
    我们把所有流量写入文件。
    """
    timestamp = int(time.time())
    filename = f"recorded_flows_{timestamp}.mitm"
    with open(filename, "wb") as f:
        writer = io.FlowWriter(f)
        for flow in flows:
            writer.add(flow)
    print(f"已保存 {len(flows)} 条流量到 {filename}")

运行方式不变:

mitmdump -s hello_world.py

操作完浏览器后,按 Ctrl+C 退出 mitmdump。你会看到它自动生成了一个 .mitm 文件。这个文件就是咱们录制的“原始素材”。

避坑指南: 我曾经在录制时忘了加 done() 函数,结果退出后啥也没保存。后来才发现,done() 是 mitmproxy 退出时自动调用的钩子。如果你需要做“退出前保存”的操作,一定要定义这个函数。

3.4 本章小结

这一章咱们干了三件事:

  • 搞清楚了 mitmproxy 的三种启动模式,以及各自适合什么场景
  • 理解了流量拦截的原理——说白了就是中间人代理
  • 写了一个真正的录制脚本,能把流量保存到文件里

你想想看,有了这个基础,后面咱们要做的“数据回放”就顺理成章了。录下来的流量,就是回放的“剧本”。

可可精品资料库