HTTPS и SSL/TLS

HTTPS — протокол защищённой передачи данных. Использует TLS (Transport Layer Security) для шифрования трафика между браузером и сервером.

Зачем нужно

Без HTTPS данные передаются открытым текстом: логины, пароли, номера карт видны любому, кто перехватит трафик (Wi-Fi в кафе, провайдер, MITM-атака). HTTPS шифрует всё. Кроме того, без HTTPS: нет Service Workers, нет HTTP/2, поисковики понижают в выдаче, браузеры показывают предупреждение «Не защищено».

Где используется

Абсолютно все публичные веб-сайты и API. С 2018 года Chrome помечает HTTP-сайты как небезопасные. Let's Encrypt сделал SSL-сертификаты бесплатными.

Предпосылки

Базовое понимание HTTP, DNS, клиент-серверная архитектура

Как работает TLS Handshake

Клиент (браузер)                        Сервер
       │                                    │
  1.   │── ClientHello ──────────────────►  │  (версия TLS, cipher suites)
       │                                    │
  2.   │◄──────────────── ServerHello ──│  │  (выбранный cipher, сертификат)
       │                                    │
  3.   │  Проверяет сертификат              │  (CA, срок, домен)
       │                                    │
  4.   │── Pre-master secret ────────────►  │  (зашифрован публичным ключом)
       │                                    │
  5.   │◄──── Оба вычисляют session key ──►│  (симметричный ключ)
       │                                    │
  6.   │════ Шифрованный трафик ═══════════│  (AES-256 и т.д.)

Упрощённо:

  1. Браузер и сервер договариваются о протоколе шифрования
  2. Сервер показывает свой сертификат (удостоверение личности)
  3. Браузер проверяет сертификат через цепочку доверия (CA)
  4. Обмениваются ключами через асимметричное шифрование
  5. Дальше общаются через быстрое симметричное шифрование

SSL-сертификаты

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

Тип Проверка Для кого
DV (Domain Validation) Владение доменом Блоги, личные сайты
OV (Organization Validation) Организация проверена Бизнес-сайты
EV (Extended Validation) Углублённая проверка Банки, крупные компании

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

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

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

# Автообновление (сертификат действует 90 дней)
sudo certbot renew --dry-run

# Cron для автообновления
0 0 1 * * certbot renew --quiet

Настройка Nginx с HTTPS

server {
    listen 80;
    server_name example.com www.example.com;
    # Редирект HTTP → HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Современные настройки TLS
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # HSTS — принудительный HTTPS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://localhost:3000;
    }
}

HTTPS в Node.js

Express + HTTPS

const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express;

// Для production: путь к Let's Encrypt сертификатам
const options = {
  key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
  cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem'),
};

https.createServer(options, app).listen(443, () => {
  console.log('HTTPS сервер на порту 443');
});

// Редирект HTTP → HTTPS
const http = require('http');
http.createServer((req, res) => {
  res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
  res.end();
}).listen(80);

Express за reverse proxy (Nginx/Cloudflare)

// Если HTTPS терминируется на прокси, Express видит HTTP
// Нужно доверять заголовку X-Forwarded-Proto
app.set('trust proxy', 1);

// Middleware для редиректа
app.use((req, res, next) => {
  if (req.headers['x-forwarded-proto'] !== 'https') {
    return res.redirect(301, `https://${req.hostname}${req.url}`);
  }
  next;
});

HSTS (HTTP Strict Transport Security)

Заголовок, который говорит браузеру: «Всегда используй HTTPS для этого домена».

// Express
app.use((req, res, next) => {
  res.setHeader(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains; preload'
  );
  next;
});

// Или через Helmet
const helmet = require('helmet');
app.use(helmet.hsts({
  maxAge: 31536000,          // 1 год
  includeSubDomains: true,
  preload: true,             // Для HSTS Preload List
}));
Параметр Значение
max-age Время в секундах (31536000 = 1 год)
includeSubDomains Распространяется на поддомены
preload Включить в предзагруженный список браузеров

Миграция HTTP → HTTPS

Чек-лист

  1. Получить SSL-сертификат (Let's Encrypt)
  2. Настроить web-сервер (Nginx/Apache)
  3. Редирект HTTP → HTTPS (301)
  4. Обновить все ссылки на HTTPS (или использовать //)
  5. Обновить sitemap.xml и robots.txt
  6. Обновить URL в Google Search Console
  7. Включить HSTS
  8. Проверить mixed content

Mixed Content

<!-- ПРОБЛЕМА: HTTPS-страница загружает HTTP-ресурсы -->
<img src="http://cdn.example.com/image.jpg">  <!-- Blocked! -->
<script src="http://cdn.example.com/app.js"></script>  <!-- Blocked! -->

<!-- РЕШЕНИЕ: протокол-относительные URL или HTTPS -->
<img src="https://cdn.example.com/image.jpg">
<script src="//cdn.example.com/app.js"></script>

<!-- CSP для блокировки mixed content -->
<meta http-equiv="Content-Security-Policy"
      content="upgrade-insecure-requests">

Проверка HTTPS

# Проверить сертификат
openssl s_client -connect example.com:443 -servername example.com

# SSL Labs — подробный анализ
# https://www.ssllabs.com/ssltest/

# Проверка срока действия
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

Частые ошибки

1. Самоподписанные сертификаты в production

Браузер показывает страшное предупреждение.
Пользователи не будут доверять сайту.
Решение: Let's Encrypt — бесплатно и автоматически.

2. Забыли обновить сертификат

# Let's Encrypt сертификаты действуют 90 дней!
# Настрой certbot renew в cron или systemd timer

3. Mixed content

HTTPS-страница подгружает ресурсы по HTTP.
Браузер блокирует или показывает предупреждение.
Решение: все ресурсы через HTTPS.

4. Устаревшие протоколы

# ПЛОХО: TLS 1.0 и 1.1 уязвимы
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# ХОРОШО: только TLS 1.2+
ssl_protocols TLSv1.2 TLSv1.3;

Практика

  1. Получи бесплатный сертификат Let's Encrypt для тестового домена (или localhost через mkcert)
  2. Настрой Nginx с HTTPS и редиректом с HTTP
  3. Добавь HSTS-заголовок в Express-приложение
  4. Проверь свой сайт на SSL Labs и исправь найденные проблемы
  5. Настрой автообновление сертификата через cron

Связанные темы

Ресурсы