Frage

Ich brauche ein Skript zu schreiben, die über HTTPS zu einem Bündel von Seiten auf unserem Unternehmens-Intranet verbindet und überprüft, ob ihre SSL-Zertifikate gültig sind; dass sie nicht abgelaufen sind, dass sie für die richtige Adresse ausgegeben werden, usw. Wir nutzen unsere eigene interne Unternehmenszertifizierungsstelle für diese Seiten, also haben wir die öffentlichen Schlüssel des CA die Zertifikate gegen.

, um zu überprüfen

Python standardmäßig akzeptiert nur und verwendet SSL-Zertifikate, wenn HTTPS verwenden, so dass selbst wenn ein Zertifikat ungültig ist, Python-Bibliotheken wie urllib2 and Twisted wird nur glücklich das Zertifikat verwenden.

Gibt es eine gute Bibliothek irgendwo, die mich zu einer Website über HTTPS verbinden lassen und sein Zertifikat auf diese Weise überprüfen?

Wie kann ich überprüfen, ein Zertifikat in Python?

War es hilfreich?

Lösung

Von Release-Version 2.7.9 / 3.4.3 auf, Python standardmäßig versucht Zertifikatsüberprüfung durchzuführen.

Dies wurde in PEP 467, vorgeschlagen, die lesenswert ist: https: //www.python. org / dev / pep / pep-0476 /

Die Änderungen betreffen alle relevanten stdlib Module (urllib / urllib2, http, httplib).

Relevante Dokumentation:

https://docs.python.org/2/library/httplib.html#httplib .HTTPSConnection

  

Diese Klasse führt nun alle notwendigen Zertifikat und Hostnamen überprüft standardmäßig. So kehren können zum vorherigen, nicht verifizierten, Verhalten ssl._create_unverified_context () auf den Kontext-Parameter übergeben werden.

https://docs.python.org/3/library/http .client.html # http.client.HTTPSConnection

  

Changed in Version 3.4.3: jetzt Diese Klasse führt alle notwendigen Zertifikat und Hostnamen überprüft standardmäßig. So kehren können zum vorherigen, nicht verifizierten, Verhalten ssl._create_unverified_context () auf den Kontext-Parameter übergeben werden.

Hinweis

, dass die neue Einbau-Prüfung basiert auf der System bereitgestellte Zertifikatsdatenbank. Im Gegensatz zu, dass die fordert Paket Schiffen sein eigenes Zertifikat zu bündeln. Vor-und Nachteile beider Ansätze sind in der diskutiert Vertrauen Datenbank -Abschnitt PEP 476 .

Andere Tipps

Ich habe eine Verteilung an den Python Package Index hinzugefügt, die die match_hostname() Funktion aus dem 3.2 ssl Paket auf frühere Versionen von Python verfügbar Pythons macht.

http://pypi.python.org/pypi/backports.ssl_match_hostname/

Sie können installieren Sie es mit:

pip install backports.ssl_match_hostname

Oder Sie können es eine Abhängigkeit in Ihrem Projekt setup.py aufgeführt machen. So oder so, kann es wie folgt verwendet werden:

from backports.ssl_match_hostname import match_hostname, CertificateError
...
sslsock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv3,
                      cert_reqs=ssl.CERT_REQUIRED, ca_certs=...)
try:
    match_hostname(sslsock.getpeercert(), hostname)
except CertificateError, ce:
    ...

Sie können Twisted-Zertifikate verwenden, um zu überprüfen. Der Haupt API ist CertificateOptions , bei dem es sich als contextFactory Argument, um verschiedene Funktionen zur Verfügung gestellt, wie listenSSL und StartTLS .

Leider weder Python noch Verdrehte kommt mit einem der Stapel von CA-Zertifikaten erforderlich, um tatsächlich tun HTTPS-Validierung, noch die HTTPS-Validierungslogik. Durch rel="noreferrer">, können Sie es nicht ganz richtig tun, nur noch aber dank der Tatsache, dass fast alle Zertifikate sind ein Thema common~~POS=TRUNC, können Sie nah genug bekommen.

Hier ist eine naive Beispielimplementierung eines Twisted-HTTPS-Client überprüft die Platzhalter und subjectAltName Erweiterungen und verwendet die Zertifikat-Authority-Zertifikate in der ‚ca-Zertifikate‘ Paket in den meisten Ubuntu-Distributionen ignoriert. Versuchen Sie es mit Ihrem Lieblings gültigen und ungültigen Zertifikat Seiten:.)

import os
import glob
from OpenSSL.SSL import Context, TLSv1_METHOD, VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, OP_NO_SSLv2
from OpenSSL.crypto import load_certificate, FILETYPE_PEM
from twisted.python.urlpath import URLPath
from twisted.internet.ssl import ContextFactory
from twisted.internet import reactor
from twisted.web.client import getPage
certificateAuthorityMap = {}
for certFileName in glob.glob("/etc/ssl/certs/*.pem"):
    # There might be some dead symlinks in there, so let's make sure it's real.
    if os.path.exists(certFileName):
        data = open(certFileName).read()
        x509 = load_certificate(FILETYPE_PEM, data)
        digest = x509.digest('sha1')
        # Now, de-duplicate in case the same cert has multiple names.
        certificateAuthorityMap[digest] = x509
class HTTPSVerifyingContextFactory(ContextFactory):
    def __init__(self, hostname):
        self.hostname = hostname
    isClient = True
    def getContext(self):
        ctx = Context(TLSv1_METHOD)
        store = ctx.get_cert_store()
        for value in certificateAuthorityMap.values():
            store.add_cert(value)
        ctx.set_verify(VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, self.verifyHostname)
        ctx.set_options(OP_NO_SSLv2)
        return ctx
    def verifyHostname(self, connection, x509, errno, depth, preverifyOK):
        if preverifyOK:
            if self.hostname != x509.get_subject().commonName:
                return False
        return preverifyOK
def secureGet(url):
    return getPage(url, HTTPSVerifyingContextFactory(URLPath.fromString(url).netloc))
def done(result):
    print 'Done!', len(result)
secureGet("https://google.com/").addCallback(done)
reactor.run()

pycurl tut dies schön.

Im Folgenden finden Sie ein kurzes Beispiel. Es wird ein pycurl.error werfen, wenn etwas faul ist, wo Sie ein Tupel mit dem Fehlercode und einer für Menschen lesbaren Nachricht erhalten.

import pycurl

curl = pycurl.Curl()
curl.setopt(pycurl.CAINFO, "myFineCA.crt")
curl.setopt(pycurl.SSL_VERIFYPEER, 1)
curl.setopt(pycurl.SSL_VERIFYHOST, 2)
curl.setopt(pycurl.URL, "https://internal.stuff/")

curl.perform()

Sie werden wahrscheinlich wollen, um weitere Optionen zu konfigurieren, wie, wo die Ergebnisse zu speichern, etc. Aber keine Notwendigkeit, das Beispiel mit Unwesentlichen Krempel.

Beispiel von dem, was Ausnahmen könnten erhoben werden:

(60, 'Peer certificate cannot be authenticated with known CA certificates')
(51, "common name 'CN=something.else.stuff,O=Example Corp,C=SE' does not match 'internal.stuff'")

Einige Links, die ich nützlich sind die libcurl-docs für setopt und getinfo gefunden.

Hier ist ein Beispiel-Skript, das Zertifikatsvalidierung zeigt:

import httplib
import re
import socket
import sys
import urllib2
import ssl

class InvalidCertificateException(httplib.HTTPException, urllib2.URLError):
    def __init__(self, host, cert, reason):
        httplib.HTTPException.__init__(self)
        self.host = host
        self.cert = cert
        self.reason = reason

    def __str__(self):
        return ('Host %s returned an invalid certificate (%s) %s\n' %
                (self.host, self.reason, self.cert))

class CertValidatingHTTPSConnection(httplib.HTTPConnection):
    default_port = httplib.HTTPS_PORT

    def __init__(self, host, port=None, key_file=None, cert_file=None,
                             ca_certs=None, strict=None, **kwargs):
        httplib.HTTPConnection.__init__(self, host, port, strict, **kwargs)
        self.key_file = key_file
        self.cert_file = cert_file
        self.ca_certs = ca_certs
        if self.ca_certs:
            self.cert_reqs = ssl.CERT_REQUIRED
        else:
            self.cert_reqs = ssl.CERT_NONE

    def _GetValidHostsForCert(self, cert):
        if 'subjectAltName' in cert:
            return [x[1] for x in cert['subjectAltName']
                         if x[0].lower() == 'dns']
        else:
            return [x[0][1] for x in cert['subject']
                            if x[0][0].lower() == 'commonname']

    def _ValidateCertificateHostname(self, cert, hostname):
        hosts = self._GetValidHostsForCert(cert)
        for host in hosts:
            host_re = host.replace('.', '\.').replace('*', '[^.]*')
            if re.search('^%s$' % (host_re,), hostname, re.I):
                return True
        return False

    def connect(self):
        sock = socket.create_connection((self.host, self.port))
        self.sock = ssl.wrap_socket(sock, keyfile=self.key_file,
                                          certfile=self.cert_file,
                                          cert_reqs=self.cert_reqs,
                                          ca_certs=self.ca_certs)
        if self.cert_reqs & ssl.CERT_REQUIRED:
            cert = self.sock.getpeercert()
            hostname = self.host.split(':', 0)[0]
            if not self._ValidateCertificateHostname(cert, hostname):
                raise InvalidCertificateException(hostname, cert,
                                                  'hostname mismatch')


class VerifiedHTTPSHandler(urllib2.HTTPSHandler):
    def __init__(self, **kwargs):
        urllib2.AbstractHTTPHandler.__init__(self)
        self._connection_args = kwargs

    def https_open(self, req):
        def http_class_wrapper(host, **kwargs):
            full_kwargs = dict(self._connection_args)
            full_kwargs.update(kwargs)
            return CertValidatingHTTPSConnection(host, **full_kwargs)

        try:
            return self.do_open(http_class_wrapper, req)
        except urllib2.URLError, e:
            if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1:
                raise InvalidCertificateException(req.host, '',
                                                  e.reason.args[1])
            raise

    https_request = urllib2.HTTPSHandler.do_request_

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print "usage: python %s CA_CERT URL" % sys.argv[0]
        exit(2)

    handler = VerifiedHTTPSHandler(ca_certs = sys.argv[1])
    opener = urllib2.build_opener(handler)
    print opener.open(sys.argv[2]).read()

oder einfach Ihr Leben leichter machen, indem Sie die Anfragen mit Bibliothek:

import requests
requests.get('https://somesite.com', cert='/path/server.crt', verify=True)

Noch ein paar Worte über seine Verwendung.

m2crypto können tun, um die Validierung . Sie können auch m2crypto Twisted verwenden wenn Sie möchten. Der Chandler Desktop-Client verwendet Verdreht für die Vernetzung und m2crypto für SSL , einschließlich Zertifikatsüberprüfung.

auf Glyphen Basierend Kommentar scheint es, wie m2crypto standardmäßig besser Zertifikatsüberprüfung tut als das, was Sie derzeit mit pyOpenSSL tun können, weil m2crypto zu subjectAltName Feld überprüft.

Ich habe gebloggt auch auf, wie a href <= "http://www.heikkitoivonen.net/blog/2008/09/30/root-certificates-for-python-programs-using-python/" rel = "nofollow noreferrer"> erhalten die Zertifikate Mozilla Firefox Schiffe mit in Python und verwendbar mit Python SSL-Lösungen.

Jython DOES ausführen Zertifikatsüberprüfung standardmäßig so Standard-Bibliothek-Modulen, zum Beispiel httplib.HTTPSConnection, etc, mit jython werden Zertifikate überprüfen und Ausnahmen für Ausfälle geben, das heißt nicht übereinstimmen Identitäten, abgelaufen Zert, etc.

In der Tat haben Sie einige zusätzliche Arbeit zu tun jython zu bekommen wie CPython zu verhalten, das heißt jython zu bekommen, um nicht certs zu überprüfen.

Ich habe eine Blog-Post geschrieben, wie Zertifikat zu deaktivieren auf jython prüfen, da es in Testphasen nützlich sein kann, etc.

Installieren eines all-vertrauensvolle Sicherheitsanbieter auf Java und Jython.
http: //jython.xhaus. com / Einbau-an-alle-vertrauend-security-Provider-on-java-and-jython /

Der folgende Code ermöglicht es Ihnen, von allen SSL-Validierungsprüfungen zu profitieren (z Datum Gültigkeit, CA Zertifikatskette ...) mit Ausnahme einer steckbaren Prüfschritt z.B. den Hostnamen oder tun andere zusätzliche Zertifikat-Verifizierungsschritte zu überprüfen.

from httplib import HTTPSConnection
import ssl


def create_custom_HTTPSConnection(host):

    def verify_cert(cert, host):
        # Write your code here
        # You can certainly base yourself on ssl.match_hostname
        # Raise ssl.CertificateError if verification fails
        print 'Host:', host
        print 'Peer cert:', cert

    class CustomHTTPSConnection(HTTPSConnection, object):
        def connect(self):
            super(CustomHTTPSConnection, self).connect()
            cert = self.sock.getpeercert()
            verify_cert(cert, host)

    context = ssl.create_default_context()
    context.check_hostname = False
    return CustomHTTPSConnection(host=host, context=context)


if __name__ == '__main__':
    # try expired.badssl.com or self-signed.badssl.com !
    conn = create_custom_HTTPSConnection('badssl.com')
    conn.request('GET', '/')
    conn.getresponse().read()

pyOpenSSL ist eine Schnittstelle zu der OpenSSL-Bibliothek. Es soll alles, was Sie zur Verfügung stellen müssen.

Ich habe das gleiche Problem, aber wollte 3rd-Party-Abhängigkeiten minimieren (weil diese einmalige Skript wurde von vielen Benutzern ausgeführt werden). Meine Lösung war ein curl Anruf zu wickeln und stellen Sie sicher, dass der Exit-Code 0 wurde. Arbeitete wie ein Charme.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top