Как сделать https-запрос с плохим сертификатом?

128

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

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структуру !?

topskip
источник
5
это не "плохой сертификат", это сертификат с другим CN. InsecureSkipVerify здесь не является законным. Вы должны установить ServerName в tls.Config в соответствии с тем, к чему вы пытаетесь подключиться. Этот пост на StackOverflow приводит к тому, что эта большая дыра в безопасности в коде Go распространяется повсюду. InsecureSkipVerify ВООБЩЕ не проверяет сертификат. Вы хотите убедиться, что сертификат был законно подписан доверенным лицом, даже если CN не соответствует имени хоста. Туннели и NATS могут законно вызвать несоответствие.
Роб

Ответы:

283

Примечание по безопасности: отключение проверок безопасности опасно, и этого следует избегать

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

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

Вы можете отключить проверку безопасности для клиента:

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
источник
17
Интересно, где поставить доверенный сертификат, чтобы соединение можно было использовать без него InsecureSkipVerify: true. Это возможно?
topskip
7
NameToCertificateможет помочь, см. tls.Configдокументацию: golang.org/pkg/crypto/tls/#Config
cyberdelia
1
Вот пример добавления настраиваемых пулов сертификатов CA: golang.org/pkg/crypto/tls/#example_Dial. Вы также можете использовать это в HTTP-клиенте.
Bitbored
7
Многие люди попадают сюда, пытаясь отключить проверку имени хоста (не отключая проверку сертификата полностью). Это неправильный ответ. Go требует, чтобы вы установили ServerName в конфигурации tls в соответствии с CN хоста, к которому вы подключаетесь, если это не имя DNS, с которым вы подключились. InsecureSkipVerify не более безопасен, чем простой старый Telnet для порта. С этим параметром аутентификация НЕ выполняется. User ServerName вместо этого!
Роб
8
Осторожно: транспорт, созданный таким образом, использует нулевые значения для большинства своих полей и поэтому теряет все значения по умолчанию . Как следует из ответа ниже , вы можете скопировать их. Нам было очень интересно выяснить, почему у нас заканчиваются файловые дескрипторы , потому что мы потеряли файлDialer.Timeout .
mknecht
26

Вот способ сделать это без потери настроек по умолчанию DefaultTransportи без поддельного запроса в соответствии с комментарием пользователя.

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

ОБНОВИТЬ

Более короткий путь:

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

Правильный способ (начиная с Go 1.13) (предоставляется ответом ниже ):

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

Предупреждение : только для тестирования / разработки. В остальном действуйте на свой страх и риск !!!

Джонатан Лин
источник
не было бы проще просто скопировать настройки транспорта по умолчанию, используя, mytransportsettings := &(*http.DefaultTransport.(*http.Transport))а затем просто изменить конфигурацию клиента TLS mytransportsettings.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}?
TheDiveO
Стоит попробовать, я думаю, я пытался убедиться, что не изменяю реальный DefaultTransport
Джонатан Лин
1
это гарантирует создание неглубокой копии, как и вы, что достаточно для обсуждаемого варианта использования. Я фактически развертываю это в рабочем коде.
TheDiveO
19

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

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

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

обкрадывать
источник
1
Вы случайно не знаете сайт, где я могу это протестировать? golang org теперь больше не выдает ошибку.
topskip
3
Этот ответ ужасно вводит в заблуждение. Если вы примете любой правильно подписанный сертификат независимо от имени хоста, вы все равно не получите реальной безопасности. Я могу легко получить действующий сертификат для любых контролируемых мной доменов; если вы просто собираетесь указать мое имя хоста для проверки TLS, вы теряете подтверждение того, что я действительно являюсь тем, кем себя называю. На этом этапе тот факт, что сертификат является «допустимым», не имеет значения; это все еще неправильно, и вы все еще уязвимы для человека посередине.
Дэниел Фаррелл
5
На предприятии, где вы фактически не доверяете ни одному из коммерческих центров сертификации (например, если они находятся за пределами США) и заменяете его одним центром сертификации для вашего предприятия, вам просто нужно убедиться, что это один из ваших сертификатов. Проверка имени хоста предназначена для ситуаций, когда пользователи ожидают совпадения имени хоста, но DNS небезопасен. На предприятии вы обычно подключаетесь к кластеру машин, где невозможно сопоставить имя хоста / IP-адрес, потому что это точные клоны машины, порожденной под новыми IP-адресами. Имя DNS находит сертификат, а cert - это идентификатор, а не имя DNS.
Роб
3
идея состоит в том, что клиентский код доверяет только ЦС предприятия. на самом деле он намного безопаснее, чем используемая в настоящее время система CA. В этом случае ничему (Thawte, Versign и т. Д.) Не доверяют. Только центр сертификации, которым мы руководствуемся. У веб-браузеров огромные списки доверия. Службы, взаимодействующие друг с другом, имеют только один ЦС в своем файле доверия.
Роб
1
спасибо @Rob. У меня идентичная установка, в которой наши службы доверяют только одному и тому же ЦС и никакому другому. Это жизненно важная информация и большая помощь.
user99999991
8

Правильный способ сделать это, если вы хотите сохранить настройки транспорта по умолчанию, сейчас (начиная с Go 1.13):

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

Transport.Clone делает глубокую копию транспорта. Таким образом, вам не нужно беспокоиться об отсутствии каких-либо новых полей, которые Transportсо временем будут добавлены в структуру.

Богдан Попа
источник
7

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

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true
Корнел Дамиан
источник
7
Doing это приведетpanic: runtime error: invalid memory address or nil pointer dereference
OscarRyz
6
Если вы не используете HTTP-запросчик по умолчанию до этого, вы должны принудительно создать поддельный запрос, чтобы он инициализировал транспорт по умолчанию,
Корнел Дамиан,
1
это не отключает проверки имени хоста. он отключает все проверки сертификатов. Go заставляет вас установить ServerName равным CN в сертификате того, к чему вы подключаетесь. InsecureSkipVerify будет подключаться к мошенническому серверу MITM, который делает вид, что пересылает реальным сервисам. ЕДИНСТВЕННОЕ законное использование InsecureSkipVerify - это захват сертификата удаленного конца и немедленное отключение.
Роб
Это нормально, только если вы ТАКЖЕ укажете VerifyPeerCertificate. Если вы просто установите InsecureSkipVerify, он вообще не будет проверять. Но был добавлен VerifyPeerCertificate, чтобы вы могли переписать проверку, чтобы делать то, что вам нужно. Вы можете игнорировать имя хоста или, возможно, даже дату истечения срока действия. Google для различных реализаций VerifyPeerCertificate, которые делают это.
Роб
0

Как правило, домен DNS URL-адреса ДОЛЖЕН совпадать с субъектом сертификата сертификата.

В прежние времена это могло быть либо путем установки домена как cn сертификата, либо путем установки домена в качестве альтернативного имени субъекта.

Поддержка cn давно устарела (с 2000 года в RFC 2818 ), и браузер Chrome больше не будет даже смотреть на cn, поэтому сегодня вам нужно иметь DNS-домен URL-адреса в качестве альтернативного имени субъекта.

RFC 6125 который запрещает проверку cn на наличие SAN для домена DNS, но не на наличие SAN для IP-адреса. RFC 6125 также повторяет, что cn устарел, что уже было сказано в RFC 2818. И должен присутствовать форум браузера центра сертификации, который в сочетании с RFC 6125 по существу означает, что cn никогда не будет проверяться на предмет доменного имени DNS.

jwilleke
источник