Jak wykonać żądanie https ze złym certyfikatem?

128

Powiedz, że chcę uzyskać https://golang.orgprogramowo. Obecnie golang.org (ssl) ma zły certyfikat, który jest wystawiany dla *.appspot.comWięc kiedy uruchamiam to:

package main

import (
    "log"
    "net/http"
)

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

Dostaję (tak jak się spodziewałem)

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

Teraz chcę sam zaufać temu certyfikatowi (wyobraź sobie certyfikat wydany przez siebie, w którym mogę zweryfikować odcisk palca itp.): Jak mogę złożyć wniosek i zweryfikować / zaufać certyfikatowi?

Prawdopodobnie muszę użyć openssl, aby pobrać certyfikat, załadować go do mojego pliku i wypełnić tls.Configstrukturę!?

topskip
źródło
5
to nie jest „zły certyfikat” to certyfikat z innym CN. InsecureSkipVerify nie jest tutaj uzasadnionym użyciem. Musisz ustawić ServerName w tls.Config, aby odpowiadał temu, z czym próbujesz się połączyć. Ten post StackOverflow powoduje, że ta duża dziura w zabezpieczeniach kodu Go rozprzestrzenia się wszędzie. InsecureSkipVerify wcale nie sprawdza certyfikatu. Chcesz sprawdzić, czy certyfikat został zgodnie z prawem podpisany przez zaufaną jednostkę, nawet jeśli numer CN nie pasuje do nazwy hosta. Tunele i NATS mogą w uzasadniony sposób powodować niezgodność.
Rob

Odpowiedzi:

283

Uwaga dotycząca bezpieczeństwa: wyłączenie kontroli bezpieczeństwa jest niebezpieczne i należy go unikać

Możesz wyłączyć globalne kontrole bezpieczeństwa dla wszystkich żądań klienta domyślnego:

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)
    }
}

Możesz wyłączyć kontrolę bezpieczeństwa dla klienta:

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
źródło
17
Zastanawiam się, gdzie umieścić zaufany certyfikat, aby można było korzystać z połączenia bez InsecureSkipVerify: true. Czy to jest możliwe?
topskip
7
NameToCertificatemoże pomóc, zajrzyj do tls.Configdokumentacji: golang.org/pkg/crypto/tls/#Config
cyberdelia
1
Oto przykład dodawania niestandardowych pul certyfikatów CA: golang.org/pkg/crypto/tls/#example_Dial Możesz także użyć tego w kliencie HTTP.
Bitbored
7
Wiele osób kończy tutaj, próbując wyłączyć sprawdzanie nazw hostów (nie wyłączając całkowicie sprawdzania certyfikatów). To jest zła odpowiedź. Go wymaga ustawienia ServerName w konfiguracji tls tak, aby pasowało do CN hosta, z którym się łączysz, jeśli nie jest to nazwa DNS, z którą się łączyłeś. InsecureSkipVerify nie jest bezpieczniejszy niż zwykły stary telnet do portu. Nie ma uwierzytelniania przy tym ustawieniu. Zamiast tego nazwa_serwera użytkownika!
Rob
8
Uwaga: tak utworzony transport używa wartości zerowych dla większości swoich pól i dlatego traci wszystkie wartości domyślne . Jak sugeruje poniższa odpowiedź , możesz chcieć je skopiować. Świetnie się bawiliśmy, zastanawiając się, dlaczego brakuje deskryptorów plików , ponieważ straciliśmy rozszerzenie Dialer.Timeout.
mknecht
26

Oto sposób na zrobienie tego bez utraty domyślnych ustawień DefaultTransporti bez potrzeby fałszywego żądania zgodnie z komentarzem użytkownika.

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},
}

AKTUALIZACJA

Krótszy sposób:

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

Właściwy sposób (od Go 1.13) (podany w odpowiedzi poniżej ):

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

Ostrzeżenie : tylko do celów testowych / programistycznych. Cokolwiek innego, kontynuuj na własne ryzyko !!!

Jonathan Lin
źródło
czy nie byłoby łatwiej po prostu skopiować domyślne ustawienia transportu za pomocą, mytransportsettings := &(*http.DefaultTransport.(*http.Transport))a następnie zmodyfikować konfigurację klienta TLS mytransportsettings.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}?
TheDiveO
Warto spróbować, myślę, że próbowałem się upewnić, że nie modyfikuję prawdziwego DefaultTransport
Jonathan Lin
1
ta , zapewnia zrobić kopię płytkie, podobnie jak ty, co jest wystarczające dla zastosowania omawianego przypadku. Właściwie wdrażam to w działającym kodzie.
TheDiveO
19

Wszystkie te odpowiedzi są błędne! Nie używaj InsecureSkipVerifydo obsługi CN, która nie pasuje do nazwy hosta. Deweloperzy Go nierozsądnie byli nieugięci, aby nie wyłączać sprawdzania nazw hostów (co ma uzasadnione zastosowania - tunele, Nats, współdzielone certyfikaty klastrów itp.), A jednocześnie mając coś, co wygląda podobnie, ale w rzeczywistości całkowicie ignoruje sprawdzanie certyfikatu. Musisz wiedzieć, że certyfikat jest ważny i podpisany certyfikatem, któremu ufasz. Ale w typowych scenariuszach wiesz, że CN nie będzie pasować do nazwy hosta, z którym się łączyłeś. Dla nich ustaw ServerNamena tls.Config. Jeśli tls.Config.ServerName== remoteServerCN, sprawdzenie certyfikatu powiedzie się. To jest to, czego chcesz. InsecureSkipVerifyoznacza, że ​​NIE ma uwierzytelnienia; i jest już gotowy na Man-In-The-Middle; pokonanie celu korzystania z TLS.

Jest jedno uzasadnione zastosowanie InsecureSkipVerify: użyj go, aby połączyć się z hostem i pobrać jego certyfikat, a następnie natychmiast się rozłączyć. Jeśli skonfigurowałeś swój kod do użycia InsecureSkipVerify, zazwyczaj dzieje się tak, ponieważ nie ustawiłeśServerName poprawnie (będzie musiał pochodzić z zmiennej env lub czegoś podobnego - nie bój się brzucha z powodu tego wymagania ... zrób to poprawnie).

W szczególności, jeśli używasz certyfikatów klienta i polegasz na nich przy uwierzytelnianiu, w zasadzie masz fałszywy login, który w rzeczywistości już się nie loguje. Odrzuć kod, który to robi InsecureSkipVerify, albo dowiesz się, co jest z nim nie tak na własnej skórze!

Obrabować
źródło
1
Czy znasz witrynę, w której mogę to przetestować? golang org nie generuje już błędów.
topskip
3
Ta odpowiedź jest strasznie myląca. Jeśli zaakceptujesz każdy ważnie podpisany certyfikat, niezależnie od nazwy hosta, nadal nie uzyskasz prawdziwego bezpieczeństwa. Mogę łatwo uzyskać ważny certyfikat dla każdej domeny, którą kontroluję; jeśli zamierzasz tylko określić moją nazwę hosta do sprawdzenia TLS, stracisz potwierdzenie, że naprawdę jestem tym, za kogo się podaje. W tym momencie fakt, że certyfikat jest „legalny”, nie ma znaczenia; to jeszcze źle i jesteś nadal podatny na człowieka w środku.
Daniel Farrell
5
W przedsiębiorstwie, w którym w rzeczywistości nie ufasz żadnemu komercyjnemu urzędowi certyfikacji (np. Jeśli znajdują się one poza Stanami Zjednoczonymi) i zastąpisz go jednym urzędem certyfikacji dla swojego przedsiębiorstwa, wystarczy sprawdzić, czy jest to jeden z Twoich certyfikatów. Sprawdzanie nazwy hosta jest stosowane w sytuacjach, w których użytkownicy oczekują, że nazwa hosta będzie zgodna, ale DNS nie jest bezpieczny. W przedsiębiorstwie zazwyczaj łączysz się z klastrem maszyn, w przypadku których nie jest możliwe dopasowanie nazwy hosta / adresu IP, ponieważ są to dokładne klony maszyny uruchomionej pod nowymi adresami IP. Nazwa dns znajduje certyfikat, a cert to identyfikator, a nie nazwa dns.
Rob
3
pomysł polega na tym, że kod klienta ufa tylko korporacyjnemu urzędowi certyfikacji. w rzeczywistości jest o wiele bezpieczniejszy niż obecnie używany system CA. W takim przypadku nic (Thawte, Versign itp.) ... nie jest zaufane. Tylko CA, który prowadzimy. Przeglądarki internetowe mają ogromne listy zaufania. Usługi komunikujące się ze sobą mają tylko jeden ośrodek certyfikacji w swoim pliku zaufania.
Rob
1
dziękuję @Rob Mam identyczną konfigurację, w której nasze usługi ufają tylko temu samemu, pojedynczemu CA i niczym więcej. To są ważne informacje i bardzo pomocne.
user99999991
8

Prawidłowy sposób, aby to zrobić, jeśli chcesz zachować domyślne ustawienia transportu, to teraz (od Go 1.13):

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

Transport.Clone tworzy głęboką kopię transportu. W ten sposób nie musisz się martwić brakiem nowych pól, które Transportz czasem zostaną dodane do struktury.

Bogdan Popa
źródło
7

Jeśli chcesz użyć domyślnych ustawień z pakietu http, więc nie musisz tworzyć nowego obiektu Transport i Client, możesz zmienić, aby zignorować weryfikację certyfikatu w następujący sposób:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true
Cornel Damian
źródło
7
W efekciepanic: runtime error: invalid memory address or nil pointer dereference
OscarRyz
6
Jeśli wcześniej nie użyjesz domyślnego requestera http, musisz wymusić fałszywe żądanie, aby domyślny transport został zainicjowany
Cornel Damian
1
to nie wyłącza sprawdzania nazwy hosta. wyłącza wszystkie kontrole certyfikatów. Idź zmusza cię do ustawienia ServerName równego CN w certyfikacie tego, z czym się łączysz. InsecureSkipVerify połączy się z fałszywym serwerem MITM, który udaje, że przekazuje do prawdziwych usług. JEDYNYM legalnym zastosowaniem InsecureSkipVerify jest pobranie certyfikatu zdalnego końca i natychmiastowe rozłączenie.
Rob
Jest to poprawne tylko wtedy, gdy RÓWNIEŻ określisz VerifyPeerCertificate. Jeśli właśnie ustawisz InsecureSkipVerify, to w ogóle nie sprawdza. Ale VerifyPeerCertificate został dodany, dzięki czemu możesz przepisać czek, aby zrobić to, czego potrzebujesz. Możesz zignorować nazwę hosta lub nawet datę ważności. Google dla różnych implementacji VerifyPeerCertificate, które to robią.
Rob
0

Ogólnie rzecz biorąc, domena DNS adresu URL MUSI być zgodna z tematem certyfikatu certyfikatu.

W przeszłości mogło to być spowodowane ustawieniem domeny jako cn certyfikatu lub ustawieniem domeny jako alternatywnej nazwy podmiotu.

Obsługa cn została wycofana przez długi czas (od 2000 r. W RFC 2818 ), a przeglądarka Chrome nie będzie już nawet patrzeć na cn, więc dziś musisz mieć domenę DNS adresu URL jako alternatywną nazwę podmiotu.

RFC 6125 zabrania sprawdzania cn, jeśli jest obecna SAN dla domeny DNS, ale nie, jeśli jest obecna SAN dla adresu IP. RFC 6125 również powtarza, że ​​cn jest przestarzałe, co zostało już powiedziane w RFC 2818. Obecne jest także forum przeglądarki urzędów certyfikacji, co w połączeniu z RFC 6125 zasadniczo oznacza, że ​​cn nigdy nie będzie sprawdzane pod kątem nazwy domeny DNS.

jwilleke
źródło