文章目录
mitmproxy原理详解
mitmproxy 是一个非常灵活的工具。准确了解代理过程的工作原理将有助于您创造性地部署它,并考虑其基本假设以及如何解决这些问题。本文档详细解释了 mitmproxy 的代理机制,从最简单的未加密显式代理开始,一直到最复杂的交互 - 在存在服务器名称指示的情况下对受 TLS 保护的流量的透明代理。
1 mitmproxy 基本原理
作为获取网络信息的第一步是将mitmproxy设置为操作系统HTTP中间代理,这样mitmproxy可以作为操作系统的网络代理服务端获取网络流量及连接信息。MITM 是中间人(Man-In-The-Middle)缩写,表示用来拦截和干扰这些理论上不透明的数据流的过程。基本思想是假装mitmproxy是客户端的服务器,假装mitmproxy是服务器的客户端,这样mitmproxy在中间就可以解码双方的流量。其中最棘手的部分是证书颁发机构系统(CA)旨在通过允许受信任的第三方对服务器的证书进行加密签名以验证它们的合法性来防止这种攻击。如果此签名不匹配或来自不受信任的一方,则安全客户端将简单地断开连接并拒绝继续。尽管 CA 系统目前存在许多缺点,但这对于尝试使用 MITM TLS 连接进行分析通常是致命的。对这个问题的回答是让mitmproxy实现一个完整的 CA 实现,可以动态生成拦截证书,并成为受信任的证书颁发机构。为了让客户端信任这些证书,开发者需要手动将 mitmproxy 注册为设备的可信 CA。
2 作为中间代理获取HTTP请求信息
2.1 应对显式HTTP请求
配置客户端使用 mitmproxy 作为显式代理是拦截流量的最简单和最可靠的方法。代理协议在HTTP RFC中进行了编纂,因此客户端和服务器的行为都得到了很好的定义,并且通常是可靠的。在与 mitmproxy 最简单的交互中,客户端直接连接到代理,并发出如下所示的请求:
GET HTTP://example.com/index.html HTTP/1.1
这是一个代理 GET 请求——普通 HTTP GET 请求的扩展形式,包括模式和主机规范,它包括 mitmproxy 需要继续进行的所有信息。
- 客户端连接到代理并发出请求。
- mitmproxy 连接到上游服务器并简单地转发请求。
2.2 应对隐式HTTP请求
当使用透明代理(隐式HTTP请求)时,连接被重定向到网络层的代理,不需要任何客户端配置。这使得透明代理成为那些无法更改客户端行为的情况的理想选择——代理遗忘的 Android 应用程序就是一个常见的例子。
为此,需要引入两个额外的组件。第一个是重定向机制,它透明地将发往 Internet 上服务器的 TCP 连接重新路由到侦听代理服务器。这通常采用与代理服务器位于同一主机上的防火墙形式——Linux上的iptables或 OSX 上的pf 。客户端启动连接后,它会发出一个普通的 HTTP 请求,它可能看起来像这样:
GET /index.html HTTP/1.1
请注意,此请求与显式代理变体不同,因为它省略了方案和主机名。那么,我们如何知道将请求转发给哪个上游主机呢?执行重定向的路由机制会提供跟踪原始目的地。每种路由机制都有不同的公开此数据的方式,因此这引入了工作透明代理所需的第二个组件:知道如何从路由器检索原始目标地址的主机模块。在 mitmproxy 中,这采用一组内置模块的形式 ,这些模块知道如何与每个平台的重定向机制对话。一旦从代理机制中获得了这些信息,这个过程就相当简单了。
透明的
- 客户端与服务器建立连接;
- 路由器将连接重定向到 mitmproxy,它通常侦听同一主机的本地端口。mitmproxy 然后咨询路由机制以确定原始目的地是什么;
- 获得了原始目的地址后就可以像应对显式HTTP请求那样处理数据转发了。
3 作为中间代理获取HTTPS请求信息
3.1 显式HTTPS请求
显式代理 HTTPS 连接的过程与HTTP完全不同。客户端连接到代理并发出如下所示的请求:
CONNECT example.com:443 HTTP/1.1
传统的代理既不能查看也不能操作 TLS 加密的数据流,因此 CONNECT 请求只是要求代理打开客户端和服务器之间的管道。这里的代理只是一个促进者——它在对内容一无所知的情况下盲目地双向转发数据。TLS 连接的协商通过此管道进行,随后的请求和响应流对代理完全不透明。
1) 获取远程主机名
要继续执行此计划,需要知道要在拦截证书中使用的域名,客户端将验证该证书是否适用于它正在连接的域,如果不是这样,则中止。乍一看,上面的 CONNECT 请求似乎给了我们所需的一切。在这个例子中,这两个值都是“example.com”。但是,如果客户端按如下方式启动连接会怎样:
CONNECT 10.1.1.1:443 HTTP/1.1
使用 IP 地址是完全合法的,因为它为我们提供了足够的信息来启动管道,即使它没有显示远程主机名。
mitmproxy 有一个巧妙的机制可以使这种上游证书嗅探变得顺畅。一旦检测到 CONNECT 请求,MITM会暂停对话的客户端部分,并同时启动到服务器的连接。我们完成与服务器的 TLS 握手,并检查它使用的证书。然后使用上游证书中的 Common Name 为客户端生成虚拟证书。这样MITM就获得正确的主机名来呈现给客户端,即使它从未被指定。
2) 处理主题备用名称SAN
有时,证书公用名实际上并不是客户端连接到的主机名。这是因为证书中的可选主题备用名称(SAN)字段允许指定任意数量的备用域。如果预期域与其中任何一个匹配,客户端将继续,即使该域与证书 CN 不匹配。这里的答案很简单:当MITM从上游证书中提取 CN 时,也提取了 SAN,并将它们添加到生成的虚拟证书中。
3) 处理服务器名称指示SNI
普通 TLS 的一大限制是每个证书都需要自己的 IP 地址。这意味着无法在具有独立证书的多个域共享相同 IP 地址的情况下进行虚拟托管。 在 IPv4 地址池迅速缩小的世界中,这是一个问题,现在有一个解决方案,即TLS 协议的服务器名称指示扩展形式。这让客户端在 TLS 握手开始时指定远程服务器名称,然后让服务器选择正确的证书来完成该过程。
SNI 打破了我们的上游证书嗅探过程,因为当我们在不使用 SNI 的情况下进行连接时,我们得到的是默认证书,这可能与客户端期望的证书无关。该解决方案是客户端连接过程的另一个复杂问题。客户端连接后,我们允许 TLS 握手继续,直到SNI值刚好传递给MITM,然后MITM暂停对话,并使用正确的 SNI 值启动上游连接,然后获取正确的上游证书,最后从中提取预期的 CN 和 SAN。
4) 显式HTTPS请求信息获取整个过程
让将上面的关键步骤有序组在一起,形成完整的显式代理 HTTPS 流。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BlhuxCOd-1687422581175)(HTTPs://docs.mitmproxy.org/stable/schematics/how-mitmproxy-works-explicit-https.png)]
- 客户端与 mitmproxy 建立连接,并发出 HTTP CONNECT 请求。
- mitmproxy 以 响应
200 Connection Established
,就好像它已经设置了 CONNECT 管道一样。 - 客户端认为它正在与远程服务器对话,并启动 TLS 连接。它使用 SNI 来指示它连接到的主机名。
- mitmproxy 连接到服务器,并使用客户端指示的 SNI 主机名建立 TLS 连接。
- 服务器用匹配的证书进行响应,其中包含生成拦截证书所需的 CN 和 SAN 值。
- mitmproxy 生成拦截证书,并继续在步骤 3 中暂停的客户端 TLS 握手。
- 客户端通过已建立的 TLS 连接发送请求。
- mitmproxy 通过步骤 4 中启动的 TLS 连接将请求传递到服务器。
3.2 应对隐式HTTPS
第一步是确定是否应该将传入连接视为 HTTPS,目的是为了使用路由机制来找出原始目标端口是什么。所有传入连接都通过不同的层,这些层可以确定要使用的实际协议。自动 TLS 检测适用于 SSLv3、TLS 1.0、TLS 1.1 和 TLS 1.2,方法是在每个连接开始时查找*ClientHello消息。*这与使用的 TCP 端口无关。
从这里开始,该过程是描述的用于透明代理 HTTP 和显式代理 HTTPS 的方法的合并。使用路由机制建立上游服务器地址,然后进行显式HTTPS连接建立CN和SAN,应对SNI。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iv7H96Yy-1687422581175)(HTTPs://docs.mitmproxy.org/stable/schematics/how-mitmproxy-works-transparent-https.png)]
- 客户端与服务器建立连接。
- 路由器将连接重定向到 mitmproxy,通常侦听同一主机的本地端口。mitmproxy 然后咨询路由机制以确定原始目的地是什么。
- 客户端认为它正在与远程服务器对话,并启动 TLS 连接。它使用 SNI 来指示它连接到的主机名。
- mitmproxy 连接到服务器,并使用客户端指示的 SNI 主机名建立 TLS 连接。
- 服务器用匹配的证书进行响应,其中包含生成拦截证书所需的 CN 和 SAN 值。
- mitmproxy 生成拦截证书,并继续在步骤 3 中暂停的客户端 TLS 握手。
- 客户端通过已建立的 TLS 连接发送请求。
- mitmproxy 通过步骤 4 中启动的 TLS 连接将请求传递到服务器。