Bagaimana cara melakukan permintaan https dengan sertifikat buruk?

128

Katakanlah saya ingin mendapatkan secara https://golang.orgterprogram. Saat ini golang.org (ssl) memiliki sertifikat buruk yang dikeluarkan untuk *.appspot.comJadi ketika saya menjalankan ini:

package main

import (
    "log"
    "net/http"
)

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

Saya mendapatkan (seperti yang saya harapkan)

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

Sekarang, saya ingin memercayai sertifikat ini sendiri (bayangkan sertifikat yang diterbitkan sendiri di mana saya dapat memvalidasi sidik jari, dll.): Bagaimana saya bisa membuat permintaan dan memvalidasi / mempercayai sertifikat?

Saya mungkin perlu menggunakan openssl untuk mengunduh sertifikat, memuatnya ke file saya dan mengisi tls.Configstruct!?

topskip
sumber
5
ini bukan "sertifikat buruk" itu adalah sertifikat dengan CN yang berbeda. InsecureSkipVerify bukan penggunaan yang sah di sini. Anda harus mengatur ServerName di tls.Config agar sesuai dengan apa yang Anda coba sambungkan. Posting StackOverflow ini menyebabkan lubang keamanan besar dalam kode Go menyebar ke mana-mana. InsecureSkipVerify tidak memeriksa sertifikat sama sekali. Yang Anda inginkan adalah memverifikasi bahwa sertifikat itu ditandatangani secara sah oleh entitas tepercaya, bahkan jika CN tidak cocok dengan nama host. Terowongan dan NATS secara sah dapat menyebabkan ini tidak cocok.
Rob

Jawaban:

283

Catatan keamanan: Menonaktifkan pemeriksaan keamanan berbahaya dan harus dihindari

Anda dapat menonaktifkan pemeriksaan keamanan secara global untuk semua permintaan klien default:

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

Anda dapat menonaktifkan pemeriksaan keamanan untuk klien:

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
sumber
17
Saya ingin tahu di mana harus meletakkan sertifikat tepercaya sehingga koneksi dapat digunakan tanpa InsecureSkipVerify: true. Apakah itu mungkin?
topskip
7
NameToCertificatemungkin membantu, lihat tls.Configdokumentasi: golang.org/pkg/crypto/tls/#Config
cyberdelia
1
Berikut adalah contoh untuk menambahkan kumpulan sertifikat CA kustom: golang.org/pkg/crypto/tls/#example_Dial Anda juga dapat menggunakannya dalam klien HTTP.
Bitbored
7
Banyak orang akhirnya mencoba menonaktifkan cek nama host (tidak sepenuhnya menonaktifkan pemeriksaan sertifikat). Ini jawaban yang salah. Go mengharuskan Anda untuk menetapkan ServerName di konfigurasi tls agar sesuai dengan CN host yang Anda sambungkan, jika itu bukan nama dns yang Anda sambungkan. InsecureSkipVerify tidak lebih aman daripada telnet biasa ke port. Tidak ada otentikasi dengan pengaturan ini. User ServerName sebagai gantinya!
Rob
8
Hati-hati: Transportasi yang dibuat seperti itu menggunakan nilai nol untuk sebagian besar bidangnya dan karenanya kehilangan semua default . Seperti yang disarankan oleh jawaban di bawah ini , Anda mungkin ingin menyalinnya. Kami bersenang - senang mencari tahu mengapa kami kehabisan deskriptor file , karena kami kehilangan Dialer.Timeout.
mknecht
26

Berikut adalah cara untuk melakukannya tanpa kehilangan pengaturan default DefaultTransport, dan tanpa memerlukan permintaan palsu sesuai komentar pengguna.

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

MEMPERBARUI

Cara yang lebih pendek:

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

Cara yang tepat (mulai Go 1.13) (disediakan oleh jawaban di bawah ):

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

Peringatan : Hanya untuk tujuan pengujian / pengembangan. Ada lagi, lanjutkan dengan risiko Anda sendiri !!!

Jonathan Lin
sumber
bukankah lebih mudah untuk hanya menyalin pengaturan transport default menggunakan mytransportsettings := &(*http.DefaultTransport.(*http.Transport))dan kemudian hanya memodifikasi konfigurasi klien TLS mytransportsettings.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}?
TheDiveO
Pantas untuk dicoba, saya pikir saya sedang berusaha untuk memastikan saya tidak memodifikasi DefaultTransport yang asli
Jonathan Lin
1
ini memastikan untuk membuat salinan yang dangkal, seperti yang Anda lakukan, yang cukup untuk kasus penggunaan yang dibahas. Saya sebenarnya menggunakan ini dalam kode kerja.
TheDiveO
19

Semua jawaban ini salah! Jangan gunakan InsecureSkipVerifyuntuk berurusan dengan CN yang tidak cocok dengan nama host. Pengembang Go dengan tidak bijaksana bersikeras untuk tidak menonaktifkan pemeriksaan hostname (yang memiliki kegunaan yang sah - terowongan, nats, sertifikat cluster bersama, dll), sementara juga memiliki sesuatu yang terlihat serupa tetapi sebenarnya sama sekali mengabaikan pemeriksaan sertifikat. Anda perlu tahu bahwa sertifikat itu valid dan ditandatangani oleh sertifikat yang Anda percayai. Tetapi dalam skenario umum, Anda tahu bahwa CN tidak akan cocok dengan nama host yang terhubung dengan Anda. Bagi mereka, mengatur ServerNametentang tls.Config. Jika tls.Config.ServerName== remoteServerCN, maka pemeriksaan sertifikat akan berhasil. Ini yang kamu inginkan. InsecureSkipVerifyberarti tidak ada otentikasi; dan itu sudah matang untuk Man-In-The-Middle; mengalahkan tujuan menggunakan TLS.

Ada satu penggunaan yang sah untuk InsecureSkipVerify: gunakan untuk terhubung ke host dan ambil sertifikatnya, lalu segera putuskan sambungan. Jika Anda mengatur kode Anda untuk digunakan InsecureSkipVerify, itu umumnya karena Anda tidak mengatur ServerNamedengan benar (itu harus berasal dari env var atau sesuatu - jangan pusing-pusing tentang persyaratan ini ... lakukan dengan benar).

Khususnya, jika Anda menggunakan sertifikat klien dan mengandalkannya untuk otentikasi, pada dasarnya Anda memiliki info masuk palsu yang tidak benar-benar masuk lagi. Menolak kode yang benar InsecureSkipVerify, atau Anda akan belajar apa yang salah dengan itu dengan cara yang sulit!

rampok
sumber
1
Apakah Anda mengetahui situs di mana saya dapat menguji ini? golang org sekarang tidak melakukan kesalahan lagi.
topskip
3
Jawaban ini sangat menyesatkan. Jika Anda menerima sertifikat yang ditandatangani secara sah apa pun nama hostnya, Anda masih belum mendapatkan keamanan yang sebenarnya. Saya dapat dengan mudah memperoleh sertifikat yang valid untuk domain apa pun yang saya kendalikan; jika Anda hanya akan menentukan nama host saya untuk pemeriksaan TLS, Anda kehilangan validasi bahwa saya benar-benar seperti yang saya katakan. Pada titik itu, fakta bahwa sertifikat itu "sah" tidak masalah; itu masih salah dan Anda masih rentan terhadap pria di tengah.
Daniel Farrell
5
Di Perusahaan di mana Anda sebenarnya tidak mempercayai salah satu CA komersial (yaitu: jika mereka di luar AS misalnya), dan ganti dengan satu CA untuk Perusahaan Anda, Anda hanya perlu memvalidasi bahwa ini adalah salah satu sertifikat Anda. Pemeriksaan nama host adalah untuk situasi di mana pengguna mengharapkan nama host cocok, tetapi DNS tidak aman. Di perusahaan, Anda biasanya terhubung ke sekelompok mesin yang tidak mungkin cocok dengan nama host / IP, karena itu adalah klon persis mesin yang dihasilkan oleh IP baru. Nama dns menemukan sertifikat, dan sertifikat adalah id, bukan nama dns.
Rob
3
idenya adalah bahwa kode klien hanya mempercayai perusahaan CA. sebenarnya jauh lebih aman daripada sistem CA yang saat ini digunakan. Dalam hal ini, tidak ada yang dapat dipercaya (Thawte, Versign, dll). Hanya CA yang kita jalankan. Browser web memiliki daftar kepercayaan yang sangat besar. Layanan berbicara satu sama lain hanya memiliki satu CA di file kepercayaannya.
Rob
1
terima kasih @Rob Saya memiliki pengaturan yang identik di mana layanan kami hanya mempercayai CA yang sama dan tidak ada yang lain. Ini adalah informasi penting dan banyak bantuan.
user99999991
8

Cara yang benar untuk melakukan ini jika Anda ingin mempertahankan pengaturan transportasi default sekarang (pada Go 1.13):

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

Transport.Clone membuat salinan transportasi yang dalam. Dengan cara ini Anda tidak perlu khawatir kehilangan bidang baru yang ditambahkan ke Transportstruct dari waktu ke waktu.

Bogdan Popa
sumber
7

Jika Anda ingin menggunakan pengaturan default dari paket http, jadi Anda tidak perlu membuat objek Transport dan Client baru, Anda dapat mengubah untuk mengabaikan verifikasi sertifikat seperti ini:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true
Cornel Damian
sumber
7
Melakukan ini akan menghasilkanpanic: runtime error: invalid memory address or nil pointer dereference
OscarRyz
6
Jika Anda tidak menggunakan pemohon http default sebelum ini, Anda harus memaksakan permintaan palsu sehingga pemindahan defaultnya diinisialisasi
Cornel Damian
1
ini tidak menonaktifkan pemeriksaan nama host. ini menonaktifkan semua pemeriksaan sertifikat. Go memaksa Anda untuk mengatur ServerName sama dengan CN di cert dari apa yang Anda sambungkan. InsecureSkipVerify akan terhubung ke server MITM jahat yang berpura-pura meneruskan ke layanan nyata. Penggunaan HANYA yang sah untuk InsecureSkipVerify adalah untuk mengambil sertifikat dari ujung jarak jauh dan segera memutuskan sambungan.
Rob
Ini hanya ok jika Anda JUGA menentukan VerifyPeerCertificate. Jika Anda baru saja menetapkan InsecureSkipVerify, itu tidak memeriksa sama sekali. Tetapi VerifyPeerCertificate telah ditambahkan sehingga Anda dapat menulis ulang cek untuk melakukan apa yang Anda butuhkan. Anda mungkin ingin mengabaikan nama host, atau bahkan mungkin tanggal kedaluwarsa. Google untuk berbagai implementasi VerifyPeerCertificate yang melakukan ini.
Rob
0

Secara umum, Domain DNS URL HARUS cocok dengan Subjek Sertifikat sertifikat.

Di masa lalu ini bisa dengan menetapkan domain sebagai cn dari sertifikat atau dengan menetapkan domain sebagai Nama Alternatif Subjek.

Dukungan untuk cn sudah lama tidak digunakan (sejak tahun 2000 di RFC 2818 ) dan browser Chrome bahkan tidak akan melihat cn lagi sehingga hari ini Anda harus memiliki Domain DNS dari URL sebagai Nama Alternatif Subjek.

RFC 6125 yang melarang memeriksa cn jika SAN untuk Domain DNS ada, tetapi tidak jika SAN untuk Alamat IP ada. RFC 6125 juga mengulangi bahwa cn sudah usang yang sudah dikatakan dalam RFC 2818. Dan Forum Browser Otoritas Sertifikasi akan hadir yang dikombinasikan dengan RFC 6125 pada dasarnya berarti bahwa cn tidak akan pernah diperiksa untuk nama Domain DNS.

jwilleke
sumber