Сертификаты и PKI — кто кому доверяет в интернете
В прошлом уроке мы говорили, что TLS-сервер представляется через сертификат, подписанный известной CA. Это звучит просто, но за этим стоит целая инфраструктура — PKI (Public Key Infrastructure). В 2026 году в браузеры и операционные системы предустановлено 100-200 root CA. Их подписи — основа доверия всего интернета.
В этом уроке: что внутри X.509 сертификата, как устроена цепочка доверия, что такое Let’s Encrypt и ACME, как сертификаты получаются и отзываются. Эти знания нужны, когда вы развёртываете сервер, troubleshoot’те TLS-ошибки или настраиваете mTLS между сервисами.
X.509: формат сертификата
Сертификат — это документ, привязывающий публичный ключ к identity (доменному имени, организации). Подписан выпустившей CA. Формат — X.509 (стандарт ITU из 1988 года, последний раз обновлён 2008).
X.509 сертификат содержит:
Subject Alternative Names (SAN) — самое важное extension. Раньше домены указывались в CN (Common Name) поле Subject, но это устарело. Современные сертификаты используют SAN — список DNS-имён, для которых сертификат валиден:
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com, DNS:api.example.com, DNS:*.example.com
*.example.com — wildcard, валидный для всех direct поддоменов (a.example.com, b.example.com), но НЕ для a.b.example.com.
В одном сертификате может быть до сотен SAN. Часто используется для группы доменов одной компании.
Цепочка доверия: root, intermediate, leaf
В TLS handshake сервер шлёт не один сертификат, а цепочку:
Зачем такая многослойность?
- Безопасность root. Root CA имеет максимально ценный private key — если его украдут, нужно перевыпускать всё. Поэтому он редко используется: подписывает только intermediates, лежит offline в HSM.
- Гибкость. Если intermediate скомпрометирован, отзывают его — root и другие intermediates продолжают работать. Damage contained.
- Браузеры доверяют только roots. Все root certs предустановлены в браузер/ОС. Чтобы выпустить intermediate, root его подписывает — браузер доверяет.
Когда клиент подключается:
- Сервер шлёт
[leaf, intermediate](root знает клиент сам). - Клиент: «Подписан ли leaf intermediate’ом? — да». «Подписан ли intermediate root’ом, которому я доверяю? — да». «Все проверки прошли? (validity, domain match, …)» — если да, валидно.
Validation: что именно проверяет браузер
При получении сертификата клиент делает обширную валидацию:
- Подпись каждой ступени цепочки. Криптографически проверяется через public key выше.
- Цепочка заканчивается на доверенном root. Браузер сверяется со своим trust store (Mozilla NSS, Apple Security, Windows Cert Store).
- Срок действия. Текущее время должно быть в [notBefore, notAfter] для каждого сертификата в цепочке.
- Совпадение имени. SAN сертификата должен содержать домен, к которому подключились.
- Key Usage / Extended Key Usage. Сертификат должен иметь EKU=serverAuth для веб-серверов.
- Revocation status. Через OCSP или CRL проверяется, не был ли сертификат отозван.
Если хоть что-то не сошлось — ошибка.
Типы валидации: DV vs OV vs EV
Сертификаты различаются по уровню проверки владельца, прежде чем CA его выдала:
Раньше EV-сертификаты показывались красиво (зелёный замок с именем компании). Считалось, что это даёт пользователю доверие. Исследования показали, что пользователи не замечают — и Chrome/Firefox убрали UI. В 2026 году EV-сертификаты не дают визуального преимущества; их используют в основном по compliance-причинам.
Practically: 95% сертификатов на интернете — бесплатные DV от Let’s Encrypt. Для большинства задач этого достаточно.
Let’s Encrypt и ACME
До 2015 года получение TLS-сертификата стоило $50-500/год и требовало ручной процедуры: генерация CSR, заполнение форм, ожидание verification, скачивание cert, установка на сервер. Это убивало adoption HTTPS на средних/мелких сайтах.
В 2015 году консорциум (EFF, Mozilla, …) запустил Let’s Encrypt — бесплатную CA с автоматизированным выпуском. Ключевой компонент — протокол ACME (Automatic Certificate Management Environment, RFC 8555).
ACME работает так:
ACME полностью автоматизирован. На вашем сервере certbot (или caddy, или acme.sh) запускается раз в день, проверяет: «срок сертификата близок к концу? — да, давай обновим». Тихо, без участия человека.
К 2026 году Let’s Encrypt выпустил больше 6 миллиардов сертификатов. Изменил интернет: HTTPS adoption с 30% (2015) до 95%+ (2026).
Современные production setup’ы используют cert-manager в Kubernetes или Caddy как reverse proxy — они автоматизируют ACME полностью. Сервер с TLS из коробки за пять минут.
Срок сертификата: 90 дней vs год
Let’s Encrypt выпускает сертификаты на 90 дней. Это намеренно мало — заставляет автоматизировать renewal. Почему короткий срок лучше:
- Лимитирование damage. Если private key скомпрометирован, окно для злоупотреблений короче.
- Forced automation. 90 дней нельзя renew руками — обязательно скриптовать. Автоматизация работает надёжнее.
- Faster propagation of changes. Если revocation в реальности не работает (см. ниже), 90-дневный срок естественно лимитирует.
Commercial CA (DigiCert, Sectigo) выпускают на год. Но индустрия движется к короче — браузеры уже не принимают сертификаты с notAfter > 397 дней (CA/Browser Forum policy).
В 2025-2026 году обсуждается 45-дневный лимит — ещё короче. Это требует от всех более robust автоматизации.
Revocation: как отозвать сертификат
Допустим, private key утёк. Что делать? Отозвать сертификат — сообщить миру, что доверия ему больше нет. Способы:
-
CRL (Certificate Revocation List). CA публикует список отозванных серийных номеров. Клиент скачивает CRL, проверяет, есть ли там cert сервера. Минусы: CRL большие (десятки МБ), клиент не часто обновляет.
-
OCSP (Online Certificate Status Protocol). Клиент при подключении к серверу спрашивает CA: «cert с серийником X отозван?». CA отвечает «valid» или «revoked». Минусы: дополнительный roundtrip, privacy (CA знает, какие сайты вы посещаете).
-
OCSP stapling. Сервер сам периодически спрашивает CA, кэширует ответ и прикладывает его к TLS handshake. Клиент видит «вот свежий OCSP-ответ от CA — cert valid». Решает обе проблемы. Стандарт в современных серверах.
Большая проблема: в реальности браузеры soft-fail OCSP. Если ответа нет (CA down, network issue) — они допускают подключение. Это значит, что revocation в большинстве случаев не работает — атакующий просто блокирует OCSP-запрос, и браузер принимает украденный cert.
Поэтому индустрия движется к short-lived certs (90 дней) как primary revocation mechanism: если cert украден — он сам истечёт через короткое время.
Browsers и CRLite (Mozilla), CRLSets (Chrome) — агрегированные списки отозванных certs, доставляемые с обновлениями браузера. Работают лучше, чем live OCSP.
Wildcard и multi-SAN сертификаты
Один сертификат может покрывать несколько доменов:
SAN: DNS:example.com
DNS:www.example.com
DNS:*.example.com <- wildcard для всех direct поддоменов
DNS:api.example.com <- explicit (даже если есть *)
DNS:*.api.example.com <- wildcard для второго уровня
Wildcard ограничения:
*.example.comвалиден дляa.example.com,b.example.com, но НЕa.b.example.com(нужен*.b.example.com).- Не может быть в TLD:
*.com— запрещено. - Single asterisk, всегда в самом начале:
*.example.com— да,a.*.com— нет.
Multi-SAN сертификат может содержать до сотен явных имён. Часто используется для группы сайтов одной компании.
Mutual TLS (mTLS) — клиент тоже подтверждает identity
В обычном TLS клиент проверяет сервер (через cert + CA), но сервер обычно НЕ проверяет клиента — только пускает любого (аутентификация делается на application level через токены).
В mTLS клиент тоже шлёт свой сертификат, сервер валидирует. Это даёт cryptographic identity клиента вместо bearer tokens. Применения:
- Service-to-service в microservices. Каждый сервис имеет свой client cert, выданный internal CA.
- API между организациями. Banking, healthcare — регуляторы требуют mTLS для inter-organization API.
- VPN-альтернатива. OpenVPN, WireGuard используют публичные ключи (по сути — упрощённый mTLS).
В Kubernetes + Service Mesh (Istio, Linkerd) — mTLS автоматический. Каждый pod имеет свой cert, обновляется автоматически. Это де-факто стандарт для security в k8s.
Реальный пример: получить cert через certbot
# Установка (Ubuntu/Debian)
sudo apt install certbot python3-certbot-nginx
# Получить сертификат для домена + автоматически настроить nginx
sudo certbot --nginx -d example.com -d www.example.com
# Что происходит под капотом:
# 1. Certbot генерирует new account key (если нет)
# 2. Регистрируется в Let's Encrypt
# 3. Делает order для example.com, www.example.com
# 4. Получает HTTP-01 challenge (token + path)
# 5. Modifies nginx config -- временно serves /.well-known/acme-challenge/TOKEN
# 6. Let's Encrypt с своих серверов делает HTTP-запрос на ваш сервер -- проверяет
# 7. Если ОК -- генерирует CSR с private key
# 8. Получает signed cert от LE
# 9. Сохраняет в /etc/letsencrypt/live/example.com/
# 10. Обновляет nginx config с путями к cert -- перезагружает nginx
# Автоматическое renewal -- через systemd timer или cron
sudo certbot renew --dry-run
# Через 60 дней (срок сертификата 90 дней, renew за 30) автоматический renew
Попробуй сам
# 1. Посмотреть на сертификат любого сайта
openssl s_client -connect github.com:443 -servername github.com < /dev/null 2>/dev/null | openssl x509 -text | head -40
# Что увидите:
# Issuer: CN=DigiCert TLS RSA SHA256 2020 CA1
# Subject: CN=github.com
# Validity: Not Before, Not After
# Subject Public Key Info: RSA 2048 bit
# X509v3 Subject Alternative Name: DNS:github.com, DNS:*.github.com
# 2. Получить только список SAN
openssl s_client -connect github.com:443 -servername github.com < /dev/null 2>/dev/null | openssl x509 -text | grep -A 5 'Subject Alternative Name'
# 3. Посмотреть полную цепочку
openssl s_client -connect github.com:443 -showcerts < /dev/null 2>/dev/null | grep -E 'subject|issuer|---'
# 4. Сертификат для одного домена через crt.sh (Certificate Transparency)
# https://crt.sh/?q=example.com -- показывает все сертификаты, выпущенные на example.com
# Это полезно для security audit: 'кто выпускал cert на наш домен?'
# 5. Создать самоподписанный сертификат для тестирования
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout /tmp/key.pem -out /tmp/cert.pem -days 365 \
-subj '/CN=localhost' -addext 'subjectAltName=DNS:localhost,DNS:test.local'
# Посмотреть, что получилось
openssl x509 -in /tmp/cert.pem -text | head -30
# 6. Использовать самоподписанный cert в Python HTTPS-сервере (только для тестов)
python3 -c "
import http.server, ssl
server = http.server.HTTPServer(('localhost', 8443), http.server.SimpleHTTPRequestHandler)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('/tmp/cert.pem', '/tmp/key.pem')
server.socket = ctx.wrap_socket(server.socket, server_side=True)
print('Listening on https://localhost:8443')
server.serve_forever()
"
# В другом терминале:
# curl --insecure https://localhost:8443 -- работает (--insecure пропускает проверку)
# curl https://localhost:8443 -- ошибка: cert не доверенный
Что вы должны вынести
- X.509 сертификат содержит public key + identity + signature CA.
- Доверие через chain: leaf -> intermediate -> root, root предустановлен в браузер.
- Subject Alternative Names (SAN) — современный способ указания доменов.
- DV / OV / EV различаются по уровню валидации. DV (Let’s Encrypt) бесплатные и быстрые.
- ACME полностью автоматизирует выпуск + renewal. certbot, caddy, cert-manager.
- Срок сертификата короткий (90 дней) — forced automation, лимит damage.
- Revocation (CRL, OCSP) в реальности не работает надёжно — short-lived certs основной механизм.
- mTLS добавляет client cert — стандарт для internal services через service mesh.
cert-manager и mTLS в Kubernetes: автоматизация PKI на уровне кластера Certificate pinning и HSTS для production API