Avatar ·

Как выполнить HTTPS-запрос в Go с самоподписанным или недействительным сертификатом?

Всем привет! Я новичок в Go и работаю над проектом, где нужно отправлять HTTPS-запросы к внутреннему серверу разработки. Проблема в том, что на этом сервере используется самоподписанный SSL-сертификат, и Go по умолчанию отклоняет такие соединения с ошибкой x509: certificate signed by unknown authority.

Мой текущий код выглядит так:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    resp, err := http.Get("https://internal-dev.example.com/api/data")
    if err != nil {
        fmt.Println("Ошибка:", err)
        return
    }
    defer resp.Body.Close()
    
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Я понимаю, что для продакшена это плохая практика, но для локальной разработки мне нужно временно игнорировать проверку сертификата. Я пробовал искать в документации, но там много сложных структур вроде http.Transport и tls.Config. Подскажите, как правильно настроить HTTP-клиент, чтобы он принимал невалидные сертификаты? Нужно ли устанавливать какие-то глобальные переменные окружения?

Заранее спасибо за помощь!

<<>>

Использование TLS-конфигурации с отключенной проверкой сертификата

В Go для решения этой задачи нужно создать собственный HTTP-клиент с кастомной TLS-конфигурацией. Основная идея — установить флаг InsecureSkipVerify: true в структуре tls.Config. Вот пошаговое решение:

package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    // Создаем TLS-конфигурацию с отключенной проверкой
    tlsConfig := &tls.Config{
        InsecureSkipVerify: true, // ВНИМАНИЕ: только для разработки!
    }
    
    // Создаем транспорт с этой конфигурацией
    transport := &http.Transport{
        TLSClientConfig: tlsConfig,
    }
    
    // Создаем клиент с кастомным транспортом
    client := &http.Client{
        Transport: transport,
    }
    
    // Выполняем запрос
    resp, err := client.Get("https://internal-dev.example.com/api/data")
    if err != nil {
        fmt.Println("Ошибка:", err)
        return
    }
    defer resp.Body.Close()
    
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Важные предостережения

  • Только для разработки: Никогда не используйте InsecureSkipVerify: true в продакшене — это делает ваше приложение уязвимым для MITM-атак.
  • Альтернатива для продакшена: Добавьте самоподписанный сертификат в доверенные корневые сертификаты системы или используйте свой CA.
  • Глобальные настройки: Не пытайтесь переопределять глобальный HTTP-клиент — создавайте отдельный клиент для таких запросов.

Как добавить доверенный сертификат (правильный способ)

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

package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    // Загружаем сертификат сервера
    caCert, err := ioutil.ReadFile("server.crt")
    if err != nil {
        panic(err)
    }
    
    // Создаем пул сертификатов
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)
    
    // Создаем TLS-конфигурацию с доверенным сертификатом
    tlsConfig := &tls.Config{
        RootCAs: caCertPool,
    }
    
    transport := &http.Transport{
        TLSClientConfig: tlsConfig,
    }
    
    client := &http.Client{
        Transport: transport,
    }
    
    resp, err := client.Get("https://internal-dev.example.com/api/data")
    if err != nil {
        fmt.Println("Ошибка:", err)
        return
    }
    defer resp.Body.Close()
    
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Этот подход безопаснее, так как вы явно указываете, каким сертификатам доверять, вместо полного отключения проверки.

<<>>

Использование переменной окружения GODEBUG

В Go есть скрытая опция для отладки TLS — переменная окружения GODEBUG с параметром x509ignoreCN=0. Однако она не отключает проверку сертификата полностью, а лишь меняет поведение при проверке Common Name (CN).

Для полного отключения проверки можно использовать другой подход — создать отдельный HTTP-клиент с помощью функции-обёртки:

package main

import (
    "crypto/tls"
    "net/http"
)

// NewInsecureClient создает HTTP-клиент без проверки сертификата
func NewInsecureClient() *http.Client {
    return &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{
                InsecureSkipVerify: true,
            },
        },
    }
}

// Использование:
func main() {
    client := NewInsecureClient()
    resp, _ := client.Get("https://example.com")
    // ...
}

Этот подход удобен, если вам нужно временно обойти проверку в нескольких местах кода. Но помните: никогда не используйте это в production — лучше настройте правильную цепочку сертификатов.