← Назад к вопросам

Что такое сертификаты?

2.8 Senior🔥 111 комментариев
#DevOps и инфраструктура#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сертификаты

Сертификат — это электронный документ, подписанный доверенным органом (центром сертификации), который подтверждает подлинность и безопасность соединения между клиентом и сервером. Главный тип — SSL/TLS сертификат.

Основные компоненты сертификата

1. Открытый ключ (Public Key)

  • Передаётся клиентам
  • Используется для шифрования сообщений
  • Может быть известен всем

2. Приватный ключ (Private Key)

  • Хранится только на сервере
  • Используется для расшифровки и подписания
  • Никогда не передаётся

3. Метаданные

  • Доменное имя (CN — Common Name)
  • Организация
  • Дата выдачи и истечения
  • Подпись центра сертификации (CA)

Как работает HTTPS/TLS

# Python: проверка сертификата при HTTPS запросе
import requests
import ssl
from urllib.request import urlopen
from urllib.error import URLError

# Запрос С проверкой сертификата (по умолчанию)
try:
    response = requests.get('https://google.com', verify=True)
    print("Сертификат валиден")
except requests.exceptions.SSLError as e:
    print(f"Ошибка сертификата: {e}")

# Опасно: игнорирование проверки сертификата (МИТМ атаки!)
response = requests.get('https://google.com', verify=False)  # ❌ Плохо!

# Проверка деталей сертификата
import ssl
import socket

hostname = 'google.com'
context = ssl.create_default_context()
with socket.create_connection((hostname, 443)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        cert = ssock.getpeercert()
        print(f"Сертификат для: {cert['subject']}")
        print(f"Действителен до: {cert['notAfter']}")
        print(f"Издатель: {cert['issuer']}")

Типы сертификатов

1. Self-Signed (самоподписанный)

  • Подписан самим владельцем
  • Браузер выдаст предупреждение
  • Используется в разработке и тестировании
# Создание самоподписанного сертификата
# openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365

import ssl
from http.server import HTTPServer, BaseHTTPRequestHandler

class SimpleHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b'Hello, HTTPS!')

# Запуск HTTPS сервера с самоподписанным сертификатом
server = HTTPServer(('localhost', 8443), SimpleHandler)
server.socket = ssl.wrap_socket(
    server.socket,
    certfile='cert.pem',
    keyfile='key.pem',
    server_side=True
)
print('HTTPS сервер запущен на https://localhost:8443')
server.serve_forever()

2. Signed by CA (подписанный центром сертификации)

  • Подписан доверенным органом (Let's Encrypt, Comodo, DigiCert)
  • Браузер доверяет такому сертификату
  • Для production используется

3. Wildcard сертификат

  • Действителен для всех поддоменов: *.example.com
  • Экономит на сертификатах
# *.example.com покроет:
# api.example.com
# app.example.com
# admin.example.com

4. SAN (Subject Alternative Name)

  • Один сертификат для нескольких доменов
  • example.com, www.example.com, api.example.com

Работа с сертификатами в Python

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from datetime import datetime, timedelta

# 1. Генерируем приватный ключ
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)

# 2. Создаём сертификат
subject = issuer = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, 'RU'),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, 'Moscow'),
    x509.NameAttribute(NameOID.LOCALITY_NAME, 'Moscow'),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'My Org'),
    x509.NameAttribute(NameOID.COMMON_NAME, 'example.com'),
])

cert = x509.CertificateBuilder().subject_name(
    subject
).issuer_name(
    issuer
).public_key(
    private_key.public_key()
).serial_number(
    x509.random_serial_number()
).not_valid_before(
    datetime.utcnow()
).not_valid_after(
    datetime.utcnow() + timedelta(days=365)
).add_extension(
    x509.SubjectAlternativeName([x509.DNSName('example.com')]),
    critical=False,
).sign(private_key, hashes.SHA256(), default_backend())

# 3. Сохраняем ключ и сертификат
with open('key.pem', 'wb') as f:
    f.write(private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    ))

with open('cert.pem', 'wb') as f:
    f.write(cert.public_bytes(serialization.Encoding.PEM))

Let's Encrypt: бесплатные сертификаты

# Установка Certbot
sudo apt-get install certbot python3-certbot-nginx

# Получение сертификата для nginx
sudo certbot certonly --nginx -d example.com -d www.example.com

# Автоматическое продление (добавляется в cron)
sudo certbot renew --dry-run

Flask с HTTPS

from flask import Flask
import ssl

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello, HTTPS!'

if __name__ == '__main__':
    # Запуск с самоподписанным сертификатом
    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ssl_context.load_cert_chain('cert.pem', 'key.pem')
    
    app.run(host='0.0.0.0', port=443, ssl_context=ssl_context)

Проверка срока действия сертификата

import ssl
import socket
from datetime import datetime

def check_cert_expiry(hostname):
    context = ssl.create_default_context()
    with socket.create_connection((hostname, 443)) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            cert = ssock.getpeercert()
            not_after_str = cert['notAfter']
            
            # Парсим дату
            not_after = datetime.strptime(not_after_str, '%b %d %H:%M:%S %Y %Z')
            days_left = (not_after - datetime.now()).days
            
            print(f"Сертификат для {hostname} действителен ещё {days_left} дней")
            
            if days_left < 30:
                print("⚠️ Внимание: срок действия сертификата истекает!")

check_cert_expiry('google.com')

Лучшие практики

  1. Никогда не коммить приватные ключи в Git

    echo "key.pem" >> .gitignore
    
  2. Используй环境 переменные для пути к сертификатам

    import os
    cert_path = os.getenv('CERT_PATH')
    key_path = os.getenv('KEY_PATH')
    
  3. Автоматизируй продление (Let's Encrypt с Certbot)

  4. Проверяй срок действия перед истечением

  5. Используй HTTPS везде (даже в разработке)

  6. Не игнорируй SSL ошибки в production

Сертификаты — основа безопасности в интернете. Правильное управление ими критично для защиты данных пользователей.

Что такое сертификаты? | PrepBro