Vraag Hoe een antwoord krijgen SSL-certificaat van aanvragen in python?


Ik probeer het SSL-certificaat van een reactie in te krijgen requests.

Wat is een goede manier om dit te doen?

Heel erg bedankt!


10
2018-06-03 18:37


oorsprong


antwoorden:


requests houdt zich opzettelijk op low-level dingen zoals dit. Normaal gesproken is het enige dat u wilt doen Controleer of de certificaten geldig zijn. Om dat te doen, gaat u gewoon door verify=True. Als u een niet-standaard cacert-bundel wilt gebruiken, kunt u dat ook doorgeven. Bijvoorbeeld:

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

Ook, requests is vooral een verzameling wikkels rond andere bibliotheken urllib3 en de stdlib's http.client (of, voor 2.x, httplib) en ssl.

Soms is het antwoord alleen maar om bij de objecten op een lager niveau te komen (bijv. resp.raw is de urllib3.response.HTTPResponse), maar in veel gevallen is dat onmogelijk.

En dit is een van die gevallen. De enige objecten die de certs ooit zien, zijn een http.client.HTTPSConnection (of een urllib3.connectionpool.VerifiedHTTPSConnection, maar dat is slechts een subklasse van de eerstgenoemde) en een ssl.SSLSocketen geen van beide bestaat meer tegen de tijd dat het verzoek terugkeert. (Zoals de naam connectionpool impliceert, de HTTPSConnection object wordt opgeslagen in een pool en kan worden hergebruikt zodra het is voltooid; de SSLSocket is een lid van de HTTPSConnection.)

U moet dus dingen patchen zodat u de gegevens in de keten kunt kopiëren. Het kan zo simpel zijn als dit:

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

Dat is niet getest, dus geen garanties; het kan zijn dat je meer dan dat moet patchen.

Ook subclassing en overriding zou waarschijnlijk schoner zijn dan monkeypatching (vooral sindsdien HTTPAdapter is ontworpen om te worden onderverdeeld).

Of, nog beter, forking urllib3 en requests, het aanpassen van je vork, en (als je denkt dat dit legitiem van pas komt) het indienen van pull-aanvragen stroomopwaarts.

Hoe dan ook, nu, vanuit uw code, kunt u dit doen:

resp.peercert

Natuurlijk wilt u misschien ook alle informatie doorgeven die nodig is om de cert te verifiëren, maar dat is nog eenvoudiger, omdat deze al door het hoogste niveau gaat.


10
2018-06-03 19:56



Dit werkt, hoewel niet mooi,:

import requests

req = requests.get('https://httpbin.org')
pool = req.connection.poolmanager.connection_from_url('https://httpbin.org')
conn = pool.pool.get()
# get() removes it from the pool, so put it back in
pool.pool.put(conn)
print(conn.sock.getpeercert())

5
2018-06-03 19:44



Beginnen, het antwoord van abarnert is erg compleet. Terwijl het voorgestelde najaagt connection-close probleem van Kalkran Ik heb eigenlijk ontdekt dat het peercert geen gedetailleerde informatie over het SSL-certificaat. Ik groef dieper in de connectie en socket info en pakte het self.sock.connection.get_peer_certificate() functie die geweldige functies bevat zoals:

  • get_subject() voor CN
  • get_notAfter() en get_notBefore() voor vervaldatums
  • get_serial_number() en get_signature_algorithm() voor crypto-gerelateerde technische details
  • ...

De code wordt nu:

import requests

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.peer_certificate = self._connection.peer_certificate
    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.peer_certificate = resp.peer_certificate
    except AttributeError:
        pass
    return response
HTTPAdapter.build_response = new_HTTPAdapter_build_response

HTTPSConnection = requests.packages.urllib3.connection.HTTPSConnection
orig_HTTPSConnection_connect = HTTPSConnection.connect
def new_HTTPSConnection_connect(self):
    orig_HTTPSConnection_connect(self)
    try:
        self.peer_certificate = self.sock.connection.get_peer_certificate()
    except AttributeError:
        pass
HTTPSConnection.connect = new_HTTPSConnection_connect

U krijgt eenvoudig toegang tot het resultaat:

r = requests.get('https://yourdomain.tld', timeout=0.1)
print('Expires on: {}'.format(r.peer_certificate.get_notAfter()))
print(dir(r.peer_certificate))

Als u, net als ik, SSL-certificaatwaarschuwingen wilt negeren, voegt u het volgende toe aan de bovenkant van het bestand en verifieert u niet SSL:

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

r = requests.get('https://yourdomain.tld', timeout=0.1, verify=False)
print(dir(r.peer_certificate))

2
2017-08-29 07:46



Beginnen, het antwoord van abarnert is erg compleet

Maar ik zou willen toevoegen dat in het geval dat je op zoek bent naar de peer cert-keten, je nog een ander stuk code zou moeten patchen

import requests
sock_requests = requests.packages.urllib3.contrib.pyopenssl.WrappedSocket
def new_getpeercertchain(self,*args, **kwargs):
    x509 = self.connection.get_peer_cert_chain()
    return x509
sock_requests.getpeercertchain = new_getpeercertchain

daarna kun je het op een heel vergelijkbare manier noemen als het geaccepteerde antwoord

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.peercertchain = self._connection.sock.getpeercertchain()
    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.peercertchain = resp.peercertchain
    except AttributeError:
        pass
    return response
HTTPAdapter.build_response = new_HTTPAdapter_build_response

je zult krijgen resp.peercertchain welke een bevat tuple van OpenSSL.crypto.X509 voorwerpen


1
2017-12-21 18:55