Як зробити запит 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.Configstruct !?


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

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.
Бітборд

7
Тут багато людей намагаються вимкнути перевірку імені хоста (не повністю відключивши перевірки сертифікатів). Це неправильна відповідь. Go вимагає, щоб ви встановили серверне ім’я в конфігурації tls, щоб він відповідав ЦН хоста, до якого ви підключаєтесь, якщо це не ім'я 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}

Правильний спосіб (станом на п. 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 нерозумно висловлювалися за те, щоб не відключати перевірки імен хостів (що має законне використання - тунелі, nats, спільні сервери кластерів тощо), а також щось схоже, але фактично повністю ігнорує перевірку сертифіката. Вам потрібно знати, що сертифікат дійсний і підписаний сертифікатом, якому ви довіряєте. Але у звичайних сценаріях ви знаєте, що 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, а cert - ідентифікатор, а не ім'я dns.
Роб

3
ідея полягає в тому, що клієнтський код довіряє лише підприємству CA. це насправді набагато безпечніше, ніж система CA, яка зараз використовується. У цьому випадку нічого (Thawte, Versign тощо) ... не довіряють. Просто CA, яким ми керуємо. Веб-браузери мають величезні списки довіри. Служби, що розмовляють між собою, мають лише один CA у своєму довіреному файлі.
Роб

1
дякую @Rob У мене ідентична установка, де наші сервіси довіряють лише тому ж, єдиному ЦА та нічого іншого. Це життєво важлива інформація та велика допомога.
користувач99999991

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
Це призведе доpanic: runtime error: invalid memory address or nil pointer dereference
ОскарРиз

6
Якщо ви до цього не використовуєте http-запитувача за замовчуванням, ви повинні змусити підробити запит, щоб його ініціалізація транспорту за замовчуванням
Cornel Damian

1
це не відключає перевірки імені хоста. він вимикає всі перевірки сертифікатів. Ідіть, примушуючи вас встановити ім'я сервера, рівне 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.