【问题标题】:Trouble passing HTTPS communication through mitmproxy通过 mitmproxy 传递 HTTPS 通信时遇到问题
【发布时间】:2016-04-23 18:47:01
【问题描述】:

我正在尝试使用mitmproxy 和 Python 3 调试 HTTPS 连接。想法如下:代理在本地运行,我告诉 Python 将其用于 HTTPS 连接; Python 必须接受mitmproxy 创建的自签名证书才能正常工作,如果一切顺利,mitmproxy 的控制台将显示解码后的请求/响应对。

我可以用 Python 3 和requests 做我想做的事,但是我必须使用标准的urllib 来做,唉,我失败了。代码如下:

#!/usr/bin/env python3

import urllib.request
import ssl

proxy = urllib.request.ProxyHandler({'https': 'localhost:8080'})
opener = urllib.request.build_opener(proxy)
urllib.request.install_opener(opener)

ssl_ctx = ssl.create_default_context()
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE
#ssl_ctx = ssl._create_unverified_context()

req = urllib.request.Request('https://github.com')

with urllib.request.urlopen(req) as res:
#with urllib.request.urlopen(req, context=ssl_ctx) as res:
    print(res.read().decode('utf8'))

执行上述代码时,我得到一个 ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] 证书验证失败 (_ssl.c:645) 错误,这意味着使用了代理,但其证书未验证。这正是我期望在这一点上发生的事情。

当我使用注释掉的行来使用ssl_context(以任何方式创建)时,我得到了我请求的页面的内容,但代理控制台从不显示解码的请求信息,它保持空白。就好像使用 ssl_context 完全绕过了代理。

有人可以帮助我并告诉我我做错了什么吗?

编辑:为了确保,我将代理端口更改为 8081,现在使用 ssl_ctx 变量的代码 not 失败并显示“连接被拒绝”(正如预期的那样),并且使用 ssl_ctx 的代码工作正常 - 这表明我的假设是根本没有使用代理。

【问题讨论】:

  • 所以我猜默认情况下这是不可能的,注释'当前 urllib.request 不支持通过代理获取 https 位置。但是,这可以通过扩展 urllib.request 来启用,如配方 [6] 中所示。在docs.python.org/3/howto/urllib2.html#id13 中是这样说的。它导致的配方适用于 Python 2,其中一个 cmets 链接到 Python 3 版本,该版本因 'TypeError: do_open() got an unexpected keyword argument 'context'' 在 urllib 代码深处的某处而失败。我不会再让它工作了,没有意义。

标签: python-3.x https proxy


【解决方案1】:

感谢您提出此问题并发布您尝试过的代码。我不确定为什么文档声称不支持通过代理获取 HTTPS,因为它确实有效。

除了ProxyHandler 之外,我还创建并安装了HTTPSHandler,而不是显式传递SSL 上下文。这对我有用(Python 3.5 + mitmproxy):

import urllib.request
import ssl

ssl_ctx = ssl.create_default_context()
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE

ssl_handler = urllib.request.HTTPSHandler(context=ssl_ctx)
proxy_handler = urllib.request.ProxyHandler({'https': 'localhost:8080'})

opener = urllib.request.build_opener(ssl_handler, proxy_handler)
urllib.request.install_opener(opener)


if __name__ == '__main__':
    req = urllib.request.Request('https://...')

    with urllib.request.urlopen(req) as res:
        print(res.read().decode('utf8'))

一旦安装了这个开启程序,它就会被用作所有使用 urllib 的请求的默认设置,即使在第三方库中也是如此——这正是我所需要的。

不过,我不确定为什么您的注释行不起作用。也许在urlopen 中传递上下文会安装一个新的开启器,它会覆盖自定义的开启器。

【讨论】:

    猜你喜欢
    • 2011-12-07
    • 1970-01-01
    • 2021-05-23
    • 2020-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-26
    • 2013-11-23
    相关资源
    最近更新 更多