> Как происходит обмен сертификатами в HTTPS (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: VK
Стек: Go
> Пример ответа
Короткий ответ
Обмен сертификатами в HTTPS - это часть TLS handshake, где сервер (и опционально клиент) предъявляет свой X.509 сертификат, подписанный доверенным CA. Клиент проверяет цепочку доверия, срок действия, domain name и revocation status. В Go это автоматически обрабатывается через crypto/tls, но важно понимать, как настроить ClientAuth для mutual TLS и как кастомизировать VerifyPeerCertificate для дополнительных проверок.
Подробное объяснение
TLS handshake начинается с ClientHello, где клиент сообщает поддерживаемые cipher suites и версии протокола. Сервер отвечает ServerHello, выбирая параметры, и отправляет Certificate message - обычно полную цепочку: leaf-сертификат, промежуточные CA, но не root (root уже в доверенном хранилище клиента).
Клиент валидирует сертификат:
- Цепочка доверия - каждый сертификат подписан предыдущим, последний - доверенным root из системного хранилища (или кастомного пула).
- Срок действия -
NotBeforeиNotAfter. - Domain name - проверка SAN (Subject Alternative Name) или CN (deprecated).
- Revocation - OCSP stapling или CRL (в Go по умолчанию не проверяется, если не настроить).
Для mutual TLS (mTLS) сервер запрашивает CertificateRequest, и клиент отправляет свой сертификат. Сервер проверяет его аналогично, плюс может проверить, что client cert подписан конкретным CA или содержит определённые поля.
В Go crypto/tls скрывает детали. tls.Config позволяет:
Certificates- серверные сертификаты.ClientCAs- пул доверенных CA для клиентских сертификатов.ClientAuth- политика (NoClientCert, RequestClientCert, RequireAnyClientCert, VerifyClientCertIfGiven, RequireAndVerifyClientCert).VerifyConnection- callback для дополнительной проверки после стандартной.VerifyPeerCertificate- raw проверка до стандартной.
Важный нюанс: Go не проверяет revocation по умолчанию. Для этого нужно использовать VerifyConnection и вручную вызывать OCSP.
На практике
Для backend-сервисов на Go обычно достаточно стандартной проверки. Но в микросервисной архитектуре часто используется mTLS для service-to-service аутентификации. В этом случае:
- Создаётся отдельный CA для внутренних сервисов.
- Каждый сервис получает сертификат, подписанный этим CA.
- В
tls.ConfigуказываетсяClientCAsс этим CA иClientAuth: tls.RequireAndVerifyClientCert. - Для дополнительной проверки (например, что сертификат принадлежит конкретному сервису) используется
VerifyConnection.
Также важно правильно настроить ServerName при исходящих соединениях, чтобы избежать ошибок валидации, особенно при использовании IP-адресов вместо DNS-имён.
Пример кода
GOpackage mainimport ("crypto/tls""crypto/x509""log""net/http")func main() {// Загрузка серверного сертификата и ключаserverCert, err := tls.LoadX509KeyPair("server.crt", "server.key")if err != nil {log.Fatal(err)}// Пул CA для клиентских сертификатов (mTLS)clientCAs := x509.NewCertPool()caPEM, err := os.ReadFile("internal-ca.crt")if err != nil {log.Fatal(err)}if !clientCAs.AppendCertsFromPEM(caPEM) {log.Fatal("failed to parse CA")}tlsConfig := &tls.Config{Certificates: []tls.Certificate{serverCert},ClientAuth: tls.RequireAndVerifyClientCert,ClientCAs: clientCAs,MinVersion: tls.VersionTLS12,VerifyConnection: func(cs tls.ConnectionState) error {// Дополнительная проверка: сертификат должен содержать O=my-servicefor _, chain := range cs.PeerCertificates {if len(chain.Subject.Organization) > 0 && chain.Subject.Organization[0] == "my-service" {return nil}}return fmt.Errorf("client cert not authorized")},}server := &http.Server{Addr: ":8443",TLSConfig: tlsConfig,}log.Fatal(server.ListenAndServeTLS("", "")) // сертификаты уже в TLSConfig}
Как отвечать на собеседовании
Начни с краткого описания handshake: ClientHello → ServerHello → Certificate → Key Exchange → Finished. Затем переходи к деталям валидации: цепочка доверия, SAN, срок действия. Упомяни, что в Go это автоматизировано, но есть нюансы: revocation не проверяется по умолчанию, mTLS требует явной настройки.
Покажи понимание trade-off: стандартная проверка достаточна для публичных сайтов, но для внутренних сервисов нужен mTLS с отдельным CA. Если спросят про производительность - упомяни session resumption и OCSP stapling.
Что проверяет интервьюер
- Понимание TLS handshake, а не только HTTPS как "замочка в браузере".
- Знание X.509: что такое цепочка доверия, SAN, self-signed.
- Практический опыт с Go:
crypto/tls,tls.Config, mTLS. - Умение объяснить, когда стандартной проверки недостаточно и как её расширить.
- Понимание безопасности: почему нельзя отключать проверку сертификатов (
InsecureSkipVerify: true) в production.
Типичные ошибки
- Путаница между
VerifyPeerCertificateиVerifyConnection- первый вызывается до стандартной проверки, второй после. - Забывают, что Go не проверяет revocation - это нужно делать вручную.
- Используют
InsecureSkipVerify: trueв production, что открывает MITM-атаки. - Не учитывают, что при mTLS сервер должен отправлять CertificateRequest, иначе клиент не пришлёт свой сертификат.
- Ошибка с
ServerNameпри подключении по IP - сертификат с DNS-именем не пройдёт валидацию. - Путают
ClientCAs(для проверки клиента) иRootCAs(для проверки сервера).
> Похожие задачи по backend
Что такое документные базы данных?
Имеет ли смысл передавать слайс по указателю в Go
Что происходит с внешним сервисом при повторных обращениях во время сбоя
Что такое селективность индекса и почему не всегда индекс используется?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью