如何从python中的请求获取响应SSL证书?

问题描述:

尝试从 requests 中的响应中获取SSL证书

Trying to get the SSL certificate from a response in requests.

这样做的好方法是什么?

What is a good way to do this?

非常感谢!

requests 故意包装像这样的低级别的东西。通常,您唯一想做的就是验证证书有效。为此,只需传递 verify = True 。如果你想使用非标准的cacert包,你也可以传递它。例如:

requests deliberately wraps up low-level stuff like this. Normally, the only thing you want to do is to verify that the certs are valid. To do that, just pass verify=True. If you want to use a non-standard cacert bundle, you can pass that too. For example:

resp = requests.get('https://example.com', verify=True, cert=['/path/to/my/ca.crt'])

此外, requests 主要是围绕其他库的一组包装器,主要是 urllib3 和stdlib的 http.client (或者,对于2.x, httplib )和 ssl

Also, requests is primarily a set of wrappers around other libraries, mostly urllib3 and the stdlib's http.client (or, for 2.x, httplib) and ssl.

有时,答案只是为了获得较低级别的对象(例如, resp.raw urllib3.response.HTTPResponse ),但在许多情况下这是不可能的。

Sometimes, the answer is just to get at the lower-level objects (e.g., resp.raw is the urllib3.response.HTTPResponse), but in many cases that's impossible.

这就是其中一种情况。唯一看到证书的对象是 http.client.HTTPSConnection (或 urllib3.connectionpool.VerifiedHTTPSConnection ,但这只是前者的一个子类)和 ssl.SSLSocket ,在请求返回时它们都不再存在。 (正如名称 connectionpool 所暗示的那样, HTTPSConnection 对象存储在池中,并且可以立即重用。完成; SSLSocket HTTPSConnection 的成员。)

And this is one of those cases. The only objects that ever see the certs are an http.client.HTTPSConnection (or a urllib3.connectionpool.VerifiedHTTPSConnection, but that's just a subclass of the former) and an ssl.SSLSocket, and neither of those exist anymore by the time the request returns. (As the name connectionpool implies, the HTTPSConnection object is stored in a pool, and may be reused as soon as it's done; the SSLSocket is a member of the HTTPSConnection.)

因此,您需要修补内容,以便可以将数据复制到链中。它可能很简单:

So, you need to patch things so you can copy the data up the chain. It may be as simple as this:

HTTPResponse = requests.packages.urllib3.response.HTTPResponse
orig_HTTPResponse__init__ = HTTPResponse.__init__
def new_HTTPResponse__init__(self, *args, **kwargs):
    orig_HTTPResponse__init__(self, *args, **kwargs)
    try:
        self.peercert = self._connection.sock.getpeercert()
    except AttributeError:
        pass
HTTPResponse.__init__ = new_HTTPResponse__init__

HTTPAdapter = requests.adapters.HTTPAdapter
orig_HTTPAdapter_build_response = HTTPAdapter.build_response
def new_HTTPAdapter_build_response(self, request, resp):
    response = orig_HTTPAdapter_build_response(self, request, resp)
    try:
        response.peercert = resp.peercert
    except AttributeError:
        pass
    return response
HTTPAdapter.build_response = new_HTTPAdapter_build_response

那是未经测试的,所以没有保证;你可能需要修补更多。

That's untested, so no guarantees; you may need to patch more than that.

此外,子类化和重写可能比monkeypatching更清晰(特别是因为 HTTPAdapter 被设计为子类)。

Also, subclassing and overriding would probably be cleaner than monkeypatching (especially since HTTPAdapter was designed to be subclassed).

或者,甚至更好,分叉 urllib3 请求 ,修改你的fork,并且(如果你认为这是合法有用的)向上游提交pull请求。

Or, even better, forking urllib3 and requests, modifying your fork, and (if you think this is legitimately useful) submitting pull requests upstream.

无论如何,现在,从你的代码中,你可以这样做:

Anyway, now, from your code, you can do this:

resp.peercert

当然,您可能还需要传递验证证书所需的所有信息,但这更容易,因为它已经通过顶层。

Of course you may also want to pass along all the information necessary to verify the cert, but that's even easier, because it already passes through the top level.