Wie mache ich eine https-Anfrage mit einem schlechten Zertifikat?

128

Angenommen, ich möchte https://golang.orgprogrammatisch werden. Derzeit hat golang.org (ssl) ein schlechtes Zertifikat, das an *.appspot.comSo ausgestellt wird, wenn ich dies ausführe:

package main

import (
    "log"
    "net/http"
)

func main() {
    _, err := http.Get("https://golang.org/")
    if err != nil {
        log.Fatal(err)
    }
}

Ich bekomme (wie ich erwartet hatte)

Get https://golang.org/: certificate is valid for *.appspot.com, *.*.appspot.com, appspot.com, not golang.org

Jetzt möchte ich diesem Zertifikat selbst vertrauen (stellen Sie sich ein selbst ausgestelltes Zertifikat vor, mit dem ich Fingerabdrücke usw. validieren kann): Wie kann ich eine Anfrage stellen und das Zertifikat validieren / vertrauen?

Ich muss wahrscheinlich openssl verwenden, um das Zertifikat herunterzuladen, es in meine Datei zu laden und tls.Configstruct zu füllen !?

topskip
quelle
5
Dies ist kein "schlechtes Zertifikat", sondern ein Zertifikat mit einem anderen CN. InsecureSkipVerify ist hier keine legitime Verwendung. Sie müssen ServerName in der Datei tls.Config so einstellen, dass es mit dem übereinstimmt, zu dem Sie eine Verbindung herstellen möchten. Dieser StackOverflow-Beitrag führt dazu, dass sich diese große Sicherheitslücke im Go-Code überall verbreitet. InsecureSkipVerify überprüft das Zertifikat überhaupt nicht. Sie möchten überprüfen, ob das Zertifikat von einer vertrauenswürdigen Entität ordnungsgemäß signiert wurde, auch wenn der CN nicht mit dem Hostnamen übereinstimmt. Tunnel und NATS können zu Recht zu einer Nichtübereinstimmung führen.
Rob

Antworten:

283

Sicherheitshinweis: Das Deaktivieren von Sicherheitsüberprüfungen ist gefährlich und sollte vermieden werden

Sie können Sicherheitsüberprüfungen global für alle Anforderungen des Standardclients deaktivieren:

package main

import (
    "fmt"
    "net/http"
    "crypto/tls"
)

func main() {
    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
    _, err := http.Get("https://golang.org/")
    if err != nil {
        fmt.Println(err)
    }
}

Sie können die Sicherheitsüberprüfung für einen Client deaktivieren:

package main

import (
    "fmt"
    "net/http"
    "crypto/tls"
)

func main() {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}
    _, err := client.Get("https://golang.org/")
    if err != nil {
        fmt.Println(err)
    }
}
Cyberdelia
quelle
17
Ich frage mich, wo ich ein vertrauenswürdiges Zertifikat ablegen soll, damit die Verbindung ohne verwendet werden kann InsecureSkipVerify: true. Ist das möglich?
Topskip
7
NameToCertificatekönnte helfen, siehe die tls.ConfigDokumentation: golang.org/pkg/crypto/tls/#Config
cyberdelia
1
Hier ist ein Beispiel für das Hinzufügen benutzerdefinierter CA-Zertifikatpools: golang.org/pkg/crypto/tls/#example_Dial Sie können dies auch in einem HTTP-Client verwenden.
Bitbored
7
Viele Leute versuchen hier, Hostnamenprüfungen zu deaktivieren (Zertifikatprüfungen nicht vollständig zu deaktivieren). Das ist die falsche Antwort. Für Go müssen Sie den Servernamen in der tls-Konfiguration so einstellen, dass er mit dem CN des Hosts übereinstimmt, zu dem Sie eine Verbindung herstellen, wenn dies nicht der DNS-Name ist, mit dem Sie verbunden sind. InsecureSkipVerify ist nicht sicherer als ein einfaches Telnet zum Port. Bei dieser Einstellung erfolgt KEINE Authentifizierung. Benutzer Servername stattdessen!
Rob
8
Achtung: Ein so erstellter Transport verwendet für die meisten seiner Felder Nullwerte und verliert daher alle Standardeinstellungen . Wie aus der folgenden Antwort hervorgeht , möchten Sie sie möglicherweise kopieren. Wir hatten viel Spaß dabei herauszufinden, warum uns die Dateideskriptoren ausgehen , weil wir die verloren haben Dialer.Timeout.
mknecht
26

Hier ist eine Möglichkeit, dies zu tun, ohne die Standardeinstellungen von zu verlieren DefaultTransportund ohne die gefälschte Anfrage gemäß Benutzerkommentar zu benötigen.

defaultTransport := http.DefaultTransport.(*http.Transport)

// Create new Transport that ignores self-signed SSL
customTransport := &http.Transport{
  Proxy:                 defaultTransport.Proxy,
  DialContext:           defaultTransport.DialContext,
  MaxIdleConns:          defaultTransport.MaxIdleConns,
  IdleConnTimeout:       defaultTransport.IdleConnTimeout,
  ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
  TLSHandshakeTimeout:   defaultTransport.TLSHandshakeTimeout,
  TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
}

AKTUALISIEREN

Kürzere Art:

customTransport := &(*http.DefaultTransport.(*http.Transport)) // make shallow copy
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

Richtiger Weg (ab Go 1.13) (siehe Antwort unten ):

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

Warnung : Nur zu Test- / Entwicklungszwecken. Alles andere erfolgt auf eigenes Risiko !!!

Jonathan Lin
quelle
Wäre es nicht einfacher, die Standardtransporteinstellungen einfach zu kopieren mytransportsettings := &(*http.DefaultTransport.(*http.Transport))und dann nur die TLS-Client-Konfiguration zu ändern mytransportsettings.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}?
TheDiveO
Einen Versuch wert, ich glaube, ich habe versucht sicherzustellen, dass ich den echten DefaultTransport nicht ändere
Jonathan Lin
1
Dies stellt sicher , dass eine flache Kopie erstellt wird, wie Sie es getan haben, was für den besprochenen Anwendungsfall ausreicht. Ich setze dies tatsächlich in Arbeitscode ein.
TheDiveO
19

Alle diese Antworten sind falsch! Verwenden Sie diese Option nicht InsecureSkipVerify, um mit einem CN umzugehen, der nicht mit dem Hostnamen übereinstimmt. Die Go-Entwickler waren unklug darauf bedacht, Hostnamenprüfungen (die legitime Verwendungszwecke haben - Tunnel, Nats, freigegebene Clusterzertifikate usw.) nicht zu deaktivieren, während sie auch etwas hatten, das ähnlich aussieht, die Zertifikatsprüfung jedoch vollständig ignoriert. Sie müssen wissen, dass das Zertifikat gültig und von einem vertrauenswürdigen Zertifikat signiert ist. In gängigen Szenarien wissen Sie jedoch, dass der CN nicht mit dem Hostnamen übereinstimmt, mit dem Sie verbunden sind. Für diejenigen, setzen Sie ServerNameauf tls.Config. Wenn tls.Config.ServerName== remoteServerCN, ist die Zertifikatprüfung erfolgreich. Das ist, was du willst. InsecureSkipVerifybedeutet, dass KEINE Authentifizierung vorliegt; und es ist reif für einen Mann in der Mitte; den Zweck der Verwendung von TLS zunichte machen.

Es gibt eine legitime Verwendung für InsecureSkipVerify: Verwenden Sie diese Option, um eine Verbindung zu einem Host herzustellen, dessen Zertifikat abzurufen und die Verbindung sofort zu trennen. Wenn Sie Ihren Code für die Verwendung InsecureSkipVerifyeinrichten, liegt dies im Allgemeinen daran, dass Sie ihn nicht ServerNamerichtig eingestellt haben (er muss von einem env var oder so stammen - machen Sie sich keine Sorgen um diese Anforderung ... machen Sie es richtig).

Insbesondere wenn Sie Client-Zertifikate verwenden und sich bei der Authentifizierung auf diese verlassen, haben Sie im Grunde eine gefälschte Anmeldung, die sich nicht mehr anmeldet. Verweigere Code, der es tut InsecureSkipVerify, oder du wirst auf die harte Tour lernen, was daran falsch ist!

rauben
quelle
1
Kennen Sie zufällig eine Site, auf der ich dies testen kann? golang org wirft jetzt keinen Fehler mehr.
Topskip
3
Diese Antwort ist furchtbar irreführend. Wenn Sie unabhängig vom Hostnamen ein gültig signiertes Zertifikat akzeptieren, erhalten Sie immer noch keine echte Sicherheit. Ich kann problemlos ein gültiges Zertifikat für alle von mir kontrollierten Domänen erhalten. Wenn Sie nur meinen Hostnamen für die TLS-Prüfung angeben, verlieren Sie die Bestätigung, dass ich wirklich der bin, von dem ich sage, dass ich ich bin. An diesem Punkt spielt die Tatsache, dass das Zertifikat "legitim" ist, keine Rolle. Es ist immer noch falsch und du bist immer noch anfällig für Menschen in der Mitte.
Daniel Farrell
5
In einem Unternehmen, in dem Sie keiner der kommerziellen Zertifizierungsstellen vertrauen (z. B. wenn sie sich beispielsweise außerhalb der USA befinden) und durch eine Zertifizierungsstelle für Ihr Unternehmen ersetzen, müssen Sie lediglich überprüfen, ob dies eines Ihrer Zertifikate ist. Die Überprüfung des Hostnamens gilt für Situationen, in denen Benutzer erwarten, dass der Hostname übereinstimmt, DNS jedoch nicht sicher ist. Im Unternehmen stellen Sie im Allgemeinen eine Verbindung zu einem Cluster von Computern her, auf denen Hostname / IP nicht übereinstimmen können, da es sich um exakte Klone eines Computers handelt, der unter neuen IP-Adressen erstellt wurde. Der DNS-Name findet das Zertifikat, und cert ist die ID, nicht der DNS-Name.
Rob
3
Die Idee ist, dass Clientcode nur der Unternehmenszertifizierungsstelle vertraut. Es ist tatsächlich weitaus sicherer als das derzeit verwendete CA-System. In diesem Fall wird nichts (Thawte, Versign usw.) ... als vertrauenswürdig eingestuft. Nur die CA, die wir betreiben. Webbrowser haben riesige Vertrauenslisten. Dienste, die miteinander kommunizieren, haben nur die eine Zertifizierungsstelle in ihrer Vertrauensdatei.
Rob
1
danke @Rob Ich habe ein identisches Setup, bei dem unsere Dienste nur der gleichen, einzelnen Zertifizierungsstelle und sonst nichts vertrauen. Dies ist eine wichtige Information und viel Hilfe.
user99999991
8

Der richtige Weg, dies zu tun, wenn Sie die Standardtransporteinstellungen beibehalten möchten, ist jetzt (ab Go 1.13):

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client = &http.Client{Transport: customTransport}

Transport.Clone erstellt eine tiefe Kopie des Transports. Auf diese Weise müssen Sie sich keine Sorgen mehr machen, dass neue Felder fehlen, die im TransportLaufe der Zeit zur Struktur hinzugefügt werden.

Bogdan Popa
quelle
7

Wenn Sie die Standardeinstellungen aus dem http-Paket verwenden möchten, damit Sie kein neues Transport- und Client-Objekt erstellen müssen, können Sie die Zertifikatüberprüfung folgendermaßen ignorieren:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true
Cornel Damian
quelle
7
Dies würde zupanic: runtime error: invalid memory address or nil pointer dereference
OscarRyz
6
Wenn Sie den Standard-http-Anforderer vorher nicht verwenden, müssen Sie eine gefälschte Anforderung erzwingen, damit der Standardtransport initialisiert wird
Cornel Damian
1
Dadurch werden die Hostnamenprüfungen nicht deaktiviert. Es deaktiviert alle Zertifikatprüfungen. Go zwingt Sie, den Servernamen in dem Zertifikat, mit dem Sie eine Verbindung herstellen, gleich dem CN zu setzen. InsecureSkipVerify stellt eine Verbindung zu einem nicht autorisierten MITM-Server her, der vorgibt, an die realen Dienste weiterzuleiten. Die EINZIGE legitime Verwendung für InsecureSkipVerify besteht darin, das Zertifikat des Remote-Endes abzurufen und die Verbindung sofort zu trennen.
Rob
Dies ist nur in Ordnung, wenn Sie AUCH ein VerifyPeerCertificate angeben. Wenn Sie nur InsecureSkipVerify festlegen, wird dies überhaupt nicht überprüft. Es wurde jedoch ein VerifyPeerCertificate hinzugefügt, damit Sie die Prüfung neu schreiben können, um das zu tun, was Sie benötigen. Möglicherweise möchten Sie den Hostnamen oder möglicherweise sogar das Ablaufdatum ignorieren. Google für verschiedene Implementierungen von VerifyPeerCertificate, die dies tun.
Rob
0

Im Allgemeinen MUSS die DNS-Domäne der URL mit dem Zertifikatsgegenstand des Zertifikats übereinstimmen.

In früheren Zeiten konnte dies entweder durch Festlegen der Domäne als cn des Zertifikats oder durch Festlegen der Domäne als alternativen Antragstellernamen erfolgen.

Die Unterstützung für cn war lange Zeit veraltet (seit 2000 in RFC 2818 ) und der Chrome-Browser wird den cn nicht mehr anzeigen. Daher müssen Sie heute die DNS-Domäne der URL als alternativen Betreff haben.

RFC 6125, der die Überprüfung des cn verbietet, wenn SAN für DNS-Domäne vorhanden ist, nicht jedoch, wenn SAN für IP-Adresse vorhanden ist. RFC 6125 wiederholt auch, dass cn veraltet ist, was bereits in RFC 2818 erwähnt wurde. Und das Browser Forum der Zertifizierungsstelle muss vorhanden sein, was in Kombination mit RFC 6125 im Wesentlichen bedeutet, dass cn niemals auf DNS-Domänennamen überprüft wird.

jwilleke
quelle