Golang: Как выполнить запрос https с плохим сертификатом?

Скажем, я хочу получить https://golang.org программно. В настоящее время golang.org(ssl) имеет плохой сертификат, который выдается *.appspot.com So, когда я запускаю это:

package main

import (
    "log"
    "net/http"
)

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

Я получаю (как и ожидалось)

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

Теперь я хочу доверять этому сертификату самостоятельно (представьте себе самовыраженный сертификат, где я могу проверить отпечаток пальца и т.д.): как я могу сделать запрос и проверить/доверять сертификату?

Мне, вероятно, нужно использовать openssl для загрузки сертификата, загрузки его в файл и заполнения tls.Config struct!?

Ответ 1

Вы можете отключить проверку безопасности (на свой страх и риск):

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

Ответ 2

Если вы хотите использовать настройки по умолчанию из пакета http, поэтому вам не нужно создавать новый объект Transport and Client, вы можете изменить игнорировать проверку сертификата следующим образом:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true

Ответ 3

Здесь вы можете сделать это, не потеряв настройки по умолчанию DefaultTransport и не нуждаясь в поддельном запросе в соответствии с комментариями пользователя.

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

// Create new Transport that ignores self-signed SSL
httpClientWithSelfSignedTLS := &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},
}

Ответ 4

Все эти ответы неверны! Не используйте InsecureSkipVerify для работы с CN, который не соответствует имени хоста. Разработчики Go были необоснованны, не отказываясь от проверки имени хоста (который имеет законное использование - туннели, nats, общие сертификаты кластера и т.д.), А также имеют нечто похожее, но фактически полностью игнорируют проверку сертификата. Вы должны знать, что сертификат действителен и подписан сертификатом, которому вы доверяете. Но в общих сценариях вы знаете, что CN не будет соответствовать имени хоста, с которым вы связаны. Для них установите ServerName на tls.Config. Если tls.Config.ServerName == remoteServerCN, то проверка сертификата будет успешной. Это то, что вы хотите. InsecureSkipVerify означает, что нет аутентификации; и он созрел для "Человек-в-середине"; победив цель использования TLS.

Существует одно законное использование для InsecureSkipVerify... Используйте его для подключения к хосту и получения его сертификата, а затем немедленно отключите его. Если вы настроите свой код на использование InsecureSkipVerify, это обычно потому, что вы не правильно установили имя_сервера (ему нужно будет перейти от env var или что-то еще - не бойтесь этого требования... сделайте это правильно).

В частности, если вы используете клиентские сертификаты и полагаетесь на них для проверки подлинности, у вас в основном есть поддельный логин, который больше не регистрируется. Код отказа, который делает InsecureSkipVerify, или вы узнаете, что с ним не так сложно!