mitmproxy server_replay 如何回放已保存的服务端响应并自动刷新过期 Cookie?
【免费下载链接】mitmproxyAn interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.项目地址: https://gitcode.com/GitHub_Trending/mi/mitmproxy
在自动化测试或接口调试中,常见的一种需求是:客户端请求继续打向线上环境(或任意代理入口),但响应全部由之前录制好的会话文件返回,避免真实请求到达上游服务器。mitmproxy 的server_replay选项就是为此设计的:它会用一组启发式规则把新进来的请求与已保存的响应做匹配,并把保存的响应返回给客户端。由于录制时处于未来的 Cookie 过期时间、expires等时间值在回放时可能已经过期,mitmproxy 默认会对这些时间字段做刷新,保证回放的响应在时间语义上与录制时一致。
原理:请求如何匹配到已保存的响应
根据 Features 文档:
server_replay选项从保存的 HTTP 会话文件中回放服务端响应,使用一组启发式规则把进来的请求与保存的响应匹配;- 默认情况下,匹配排除请求头,只使用URL 和请求方法做匹配。这使得在请求头自然变化的情况下(例如换了 User-Agent)也能回放成功;
- 匹配规则可以通过
server_replay前缀下的一组选项定制,包括指定要参与匹配的请求头、要忽略的请求参数等。
从实现代码 serverplayback.py 可以看到,匹配 key 的构成由以下选项控制:
| 选项 | 默认值 | 作用 |
|---|---|---|
server_replay_ignore_content | false | 忽略请求内容后参与匹配(不忽略时内容会进入匹配 key) |
server_replay_ignore_params | 空 | 匹配时忽略的 URL 查询参数 |
server_replay_ignore_payload_params | 空 | 忽略请求体中application/x-www-form-urlencoded或multipart/form-data里的指定参数 |
server_replay_ignore_host | false | 忽略请求目标主机 |
server_replay_ignore_port | false | 忽略请求目标端口 |
server_replay_use_headers | 空 | 指定哪些请求头必须参与匹配 |
server_replay_reuse | false | 匹配使用一条保存的响应后不从状态中移除,同一响应可被回放多次;默认为每条响应只用一次 |
server_replay_extra | forward | 找不到可回放响应时的行为,可选forward、kill、204、400、404、500(数字值会返回对应的空 HTTP 响应) |
自动刷新:date、expires、last-modified 与 Cookie 过期时间
Features 文档 的 Response refreshing 一节说明了默认行为:
直接不改就回放服务端响应常常会导致意外行为。例如录制时还处于未来的 cookie 超时时间,在回放时可能已经过去了。默认情况下,mitmproxy 会在把响应发给客户端之前先刷新它。date、expires和last-modified头都会被更新为与录制时相同的相对时间偏移——录制时它们在过去,回放时也在过去,反之亦然。Cookie 的过期时间以相同方式更新。
实现位于 http.py 的 Response.refresh:以“当前时间 − 录制时的timestamp_start”得到时间差,然后对date、expires、last-modified头逐个加上该偏移,并对每个set-cookie头调用refresh_set_cookie_header刷新 Cookie 过期属性。
这个行为由server_replay_refresh选项控制,默认开启(true)。如果希望原样回放、不刷新任何时间字段,将其设为false即可。
操作步骤
1. 准备一个已保存的会话文件
先用 mitmdump 录制一次 HTTP 会话并保存到文件(录制阶段浏览器/客户端需指向 mitmproxy 代理,若走 TLS 需先配置好 mitmproxy 的证书,参见 Certificates 文档):
mitmdump -w session.mitm录制完成后得到会话文件session.mitm。
2. 启动带 server_replay 的代理
cmdline.py 中注册了相关命令行参数,主路径是用--server-replay(短选项-S,见 cmdline.py 第 86 行)指定保存的会话文件:
mitmdump --server-replay session.mitm启动后,所有进入该代理的 HTTP 请求都会先尝试与session.mitm中的保存响应匹配;匹配成功的请求直接返回保存的响应(经过默认的时间刷新),不会发往上游服务器。
如果想同时定制“未匹配请求”的处理方式,例如未匹配的请求直接断开而不是转发到上游:
mitmdump --server-replay session.mitm --server-replay-extra kill或在需要重复回放同一条响应时开启复用:
mitmdump -S session.mitm --server-replay-reuse3. 在 mitmproxy 交互界面中操作
也可以用 mitmproxy 控制台界面(mitmproxy)动态开始/停止回放。界面按键列表里S对应Start server replay(见 mitmproxy_user_interface.cast 录制文件),其背后对应的命令在 serverplayback.py 中定义:
replay.server.file <path>:从文件加载并回放服务端响应;replay.server.add <flows>:把当前查看的 flow 的响应追加进回放列表;replay.server <flows>:用指定 flow 重新设置回放内容;replay.server.stop:停止回放并清空回放状态。
验证回放是否生效
- 在 mitmproxy 界面(或命令行提示符)中执行
replay.server.count,返回当前回放状态中可用的 flow 数量(见 serverplayback.py 第 173-175 行),确认会话文件已成功加载。 - 通过代理发出一个录制时存在过的请求,观察代理返回的是保存的响应而非上游实时响应。
- 检查响应头:
date、expires、last-modified以及set-cookie中的过期时间应被刷新为相对录制时刻相同的偏移(例如录制时expires在请求时间之后 1 小时,回放时同样在请求时间之后约 1 小时)。如果刷新行为不符合预期,确认是否误设了--server-replay-refresh false。 - 发一个录制文件中不存在的请求,按
server_replay_extra的取值观察行为:默认forward时请求被转发到上游(并在日志中警告server_playback: returned ... non-replay request仅在设置为数字/kill 时出现对应警告);kill时连接被断开;设为204/400/404/500时返回对应状态码的空响应。
限制与边界
- 匹配是启发式的:两个 URL 与请求方法相同但请求内容不同的请求会命中同一条保存响应。若需区分,用
server_replay_use_headers、server_replay_ignore_params等选项收紧或放宽匹配 key;这些选项在运行中变化时,recompute_hashes 会基于新规则重建回放索引。 - 默认每条保存响应只被回放一次(
server_replay_reuse为false);同一请求反复发且期望每次拿到保存的响应时,需开启--server-replay-reuse。 server_replay_kill_extra与server_replay_nopop已弃用,分别用server_replay_extra='kill'和server_replay_reuse替代,使用时 mitmproxy 会打印弃用提示。- 对于部分协议,Protocols 文档 明确标注了 “Replay: Client or server replay is not possible yet”,server_replay 面向的是可解密的 HTTP 会话,不适用于不支持回放的协议。
【免费下载链接】mitmproxyAn interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.项目地址: https://gitcode.com/GitHub_Trending/mi/mitmproxy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考