mitmproxy(Man-in-the-middle attack,中间人攻击代理)是一款提供交互能力的抓包工具,可以用来拦截、修改、保存 HTTP/HTTPS 请求,对于爬虫尤其是基于APP的爬虫来说,是必不可少的一款神器。mitmproxy 基于Python开发,可以通过Python代码对请求和响应进行自定义过滤和修改。
1. 安装
mitmproxy安装
代码语言:javascript复制>> pip install mitmproxy
>> mitmproxy --version
Mitmproxy: 6.0.2
Python: 3.8.6
OpenSSL: OpenSSL 1.1.1i 8 Dec 2020
Platform: macOS-10.16-x86_64-i386-64bit
证书安装
Chrome输入mimt.it,则打开如下页面,根据操作系统选择对应的证书。
如果是Mac电脑,需要将证书添加为信任文件方可生效。
IPhone的话要开启对证书信任的按钮。设置里面搜索信任。否则无法获取https请求。
2. 启动
mitmproxy 提供了三种启动模式:
- mitmproxy -> 提供一个可交互的命令行界面。
- mitmdump -> 提供一个简单的终端输出。
屏幕录制2021-05-14 23.59.51.mov
- mitmweb -> 提供一个浏览器界面。
3. 功能介绍
- HTTP 回放
可以将拦截的http请求,进行request内容修改后重新回放。
屏幕录制2021-05-15 00.06.07.mov
- 设置过滤。类似postman interceptor的filter功能。
如果是mimtweb方式开启,则可以在GUI上配置过滤信息。
如果是通过mitmdump开启,则可以使用过滤表达式进行过滤。
a | Match asset in response: CSS, Javascript, Flash, images. |
---|---|
b regex | Body |
bq regex | Request body |
bs regex | Response body |
c int | HTTP response code |
d regex | Domain |
dst regex | Match destination address |
e | Match error |
h regex | Header |
hq regex | Request header |
hs regex | Response header |
http | Match HTTP flows |
m regex | Method |
marked | Match marked flows |
q | Match request with no response |
s | Match response |
src regex | Match source address |
t regex | Content-type header |
tcp | Match TCP flows |
tq regex | Request Content-Type header |
ts regex | Response Content-Type header |
u regex | URL |
websocket | Match WebSocket flows (and HTTP-WebSocket handshake flows) |
! | unary not |
& | and |
| | or |
举例:URL中仅包含 baidu.com;
代码语言:javascript复制mitmproxy -p 8080 u baidu.com
terminal仅展示baidu.com的http请求。(ps:只是过滤展示而已)
4. 进阶-插件开发
4.1插件
Mitmproxy的插件机制由一组API组成,插件通过响应事件与mitmproxy进行交互,从而使它们能够改变mitmproxy的行为。
插件是mitmproxy的强大组成部分。实际上,许多mitmproxy自己的功能是在一组内置插件中定义的,实现了从反缓存和粘性Cookie之类的功能到我们的入门Webapp的所有功能。内置的插件有助于进行指导性阅读, 你很快就会发现,相当复杂的功能通常可以归结为非常小的,完全独立的模块。Mitmproxy为第三方脚本编写者和扩展程序提供了与内置功能完全相同的一组工具。
创建一个python脚本 anatomy.py。
代码语言:javascript复制from mitmproxy import ctx
class Counter:
def __init__(self):
self.num = 0
def request(self, flow):
self.num = self.num 1
ctx.log.info("We've seen %d flows" % self.num)
addons = [
Counter()
]
上面是一个简单的插件,用于跟踪我们拦截HTTP请求的数量。每次看到新的HTTP请求时,它都会使用mitmproxy的内部日志记录机制来打印出来。可以在交互式工具的事件日志中或mitmdump的控制台中看到输出结果。
在示例中使用mitmpdump指令:
代码语言:javascript复制> mitmdump -s ./anatomy.py
4.2配置
mitmproxy的核心是全局选项存储,其中包含确定mitmproxy及其附加组件行为的设置。可以从配置文件中读取选项,在命令行上进行设置,并由用户即时进行交互更改。
下面是在更改响应headers信息,增加计数的例子:
代码语言:javascript复制from mitmproxy import ctx
class AddHeader:
def __init__(self):
self.num = 0
def load(self, loader):
loader.add_option(
name = "addheader",
typespec = bool,
default = False,
help = "Add a count header to responses",
)
def response(self, flow):
if ctx.options.addheader:
self.num = self.num 1
flow.response.headers["count"] = str(self.num)
addons = [
AddHeader()
]
代码语言:javascript复制>> env http_proxy=http://localhost:8080 curl -I http://baidu.com
HTTP/1.1 200 OK
Date: Sat, 15 May 2021 02:41:52 GMT
Server: Apache
Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
ETag: "51-47cf7e6ee8400"
Accept-Ranges: bytes
Content-Length: 81
Cache-Control: max-age=86400
Expires: Sun, 16 May 2021 02:41:52 GMT
Connection: Keep-Alive
Content-Type: text/html
count: 5
4.3指令
命令允许用户与插件进行积极的交互-查询其状态。像options一样,将键入命令,并在运行时检查命令的调用和返回的数据。命令是一个非常强大的结构-例如mitmproxy控制台中的所有用户交互都是通过将命令绑定到键来构建的。如下插件,开发一个指令 myaddon.addheader,在terminal输入即可向请求头增加一个key: myheader
代码语言:javascript复制import typing
from mitmproxy import command
from mitmproxy import ctx
from mitmproxy import flow
class MyAddon:
@command.command("myaddon.addheader")
def addheader(self, flows: typing.Sequence[flow.Flow]) -> None:
for f in flows:
f.request.headers["myheader"] = "value"
ctx.log.alert("done")
addons = [
MyAddon()
]
代码语言:javascript复制>> mitmproxy -s ./examples/addons/commands-flows.py
>> :myaddon.addheader @focus
4.4脚本
addons机制具有一种非常便捷的方式,可以将模块作为一个整体视为一个addon对象。这使我们可以将事件处理程序函数放在模块作用域中。例如,下面是一个完整的脚本,它向每个请求头添加键值对。
代码语言:javascript复制def request(flow):
flow.request.headers["myheader"] = "value"
拦截特定URL的请求并发送任意响应的示例:
代码语言:javascript复制from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
if flow.request.pretty_url == "http://example.com/path":
flow.response = http.HTTPResponse.make(
200, # (optional) status code
b"Hello World", # (optional) content
{"Content-Type": "text/html"} # (optional) headers
)
可以基于mitmproxy支持的所有事件开发自己的脚本;附 /api/events.html
5. 项目实践
动态获取知乎视频专栏信息,不停刷知乎就会不停打印输出。
代码语言:javascript复制#!/usr/bin/env python3
# _*_ coding: utf-8 _*_
import json
import re
from mitmproxy import ctx
class zhihu:
def response(self, flow):
# 提取请求的 url 地址
request_url = flow.request.url
# 通过 zhihu 字符串,过滤出 知乎APP 的请求和返回数据
if bool(re.search(r"zhihu", request_url)):
print("request_url >>> ", request_url)
response_body = flow.response.text
response_url = flow.request.url
print("response_url >>> ", response_url)
ctx.log.info("打印输出:" response_body)
data = json.loads(response_body)
try:
ware_infos = data.get("data")
av_info = {}
# 以下逻辑需要提前熟悉接口响应信息结构,再做出解析
if ware_infos is not None:
for ware_info in ware_infos:
av_info["标题"] = ware_info['card']['title']['plain_text']
av_info["视频地址"] = ware_info['card']['action']['intent_url']
av_info["UP主"] = ware_info['card']['author']['name']
print(av_info)
except:
ctx.log.info("跳过")
addons = [
zhihu()
]
执行
代码语言:javascript复制 mitmdump -s jingdong.py u zhihu.com