mitmproxy(Man-in-the-middle attack,中间人攻击代理)是一款提供交互能力的抓包工具,可以用来拦截、修改、保存 HTTP/HTTPS 请求,对于爬虫尤其是基于APP的爬虫来说,是必不可少的一款神器。mitmproxy 基于Python开发,可以通过Python代码对请求和响应进行自定义过滤和修改。
2022-12-18T13:02:53.png

2022-12-18T13:02:58.png

  1. 安装
    mitmproxy安装

    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
    证书安装
    2022-12-18T13:03:41.png
    Chrome输入mimt.it,则打开如下页面,根据操作系统选择对应的证书。
    2022-12-18T13:03:52.png

如果是Mac电脑,需要将证书添加为信任文件方可生效。

IPhone的话要开启对证书信任的按钮。设置里面搜索信任。否则无法获取https请求。

  1. 启动
    mitmproxy 提供了三种启动模式:

mitmproxy -> 提供一个可交互的命令行界面。
2022-12-18T13:04:07.png

mitmdump -> 提供一个简单的终端输出。

mitmweb -> 提供一个浏览器界面。
2022-12-18T13:04:26.png

  1. 功能介绍
    HTTP 回放
    可以将拦截的http请求,进行request内容修改后重新回放。

设置过滤。类似postman interceptor的filter功能。
如果是mimtweb方式开启,则可以在GUI上配置过滤信息。

2022-12-18T13:04:43.png

如果是通过mitmdump开启,则可以使用过滤表达式进行过滤。
举例:URL中仅包含 baidu.com;

mitmproxy -p 8080 u baidu\.com

terminal仅展示baidu.com的http请求。(ps:只是过滤展示而已)

  1. 进阶-插件开发
    4.1插件

Mitmproxy的插件机制由一组API组成,插件通过响应事件与mitmproxy进行交互,从而使它们能够改变mitmproxy的行为。

插件是mitmproxy的强大组成部分。实际上,许多mitmproxy自己的功能是在一组内置插件中定义的,实现了从反缓存和粘性Cookie之类的功能到我们的入门Webapp的所有功能。内置的插件有助于进行指导性阅读, 你很快就会发现,相当复杂的功能通常可以归结为非常小的,完全独立的模块。Mitmproxy为第三方脚本编写者和扩展程序提供了与内置功能完全相同的一组工具。

创建一个python脚本 anatomy.py。

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指令:

> mitmdump -s ./anatomy.py

4.2配置

mitmproxy的核心是全局选项存储,其中包含确定mitmproxy及其附加组件行为的设置。可以从配置文件中读取选项,在命令行上进行设置,并由用户即时进行交互更改。

下面是在更改响应headers信息,增加计数的例子:

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()
]
>> 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

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()
]
>> mitmproxy -s ./examples/addons/commands-flows.py
>> :myaddon.addheader @focus

2022-12-18T13:06:55.png
4.4脚本

addons机制具有一种非常便捷的方式,可以将模块作为一个整体视为一个addon对象。这使我们可以将事件处理程序函数放在模块作用域中。例如,下面是一个完整的脚本,它向每个请求头添加键值对。

def request(flow):
    flow.request.headers["myheader"] = "value"

拦截特定URL的请求并发送任意响应的示例:

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

  1. 项目实践
    动态获取知乎视频专栏信息,不停刷知乎就会不停打印输出。

    #!/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()
   ]

执行

mitmdump -s jingdong.py u zhihu.com

2022-12-18T13:08:25.png

最后修改:2022 年 12 月 18 日
如果觉得我的文章对你有用,请随意赞赏