Перейти к основному содержимому

Лучшие практики SSL/TLS

Рекомендации по настройке безопасных SSL/TLS соединений.

Генерация DH параметров

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

Конфигурация Nginx для SSL

Базовая SSL конфигурация

# SSL Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# SSL Session
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;

# DH Parameters
ssl_dhparam /etc/ssl/certs/dhparam.pem;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" always;

Полная SSL конфигурация сайта

server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}

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

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

# SSL Configuration
include /etc/nginx/snippets/ssl-params.conf;

# Root directory
root /var/www/html;
index index.php index.html index.htm;

# Security headers
include /etc/nginx/snippets/security-headers.conf;

# PHP Processing
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;

# Security
fastcgi_param HTTPS on;
fastcgi_param SERVER_PORT 443;
fastcgi_param REQUEST_SCHEME https;
}

# Security
location ~ /\. {
deny all;
}

location ~* \.(log|conf|htaccess|htpasswd|ini|sql)$ {
deny all;
}
}

Создание сниппетов Nginx

SSL параметры

Создаем /etc/nginx/snippets/ssl-params.conf:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;

ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

Заголовки безопасности

Создаем /etc/nginx/snippets/security-headers.conf:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

# CSP Header - настроить под ваше приложение
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;

Валидация HTTP

Создание сертификата через HTTP валидацию:

sudo certbot certonly --webroot \
-w /var/www/html \
-d example.com \
-d www.example.com \
--email admin@example.com \
--agree-tos \
--no-eff-email

Nginx конфигурация для HTTP валидации

server {
listen 80;
server_name example.com www.example.com;

# Acme Challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
try_files $uri =404;
}

# Redirect все остальное на HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}

Валидация DNS

Cloudflare DNS API

# Устанавливаем плагин Cloudflare
sudo apt install python3-certbot-dns-cloudflare

# Создаем файл с API ключом
sudo nano /etc/letsencrypt/cloudflare.ini

Содержимое файла:

dns_cloudflare_email = your-email@example.com
dns_cloudflare_api_key = your-global-api-key

Или с токеном:

dns_cloudflare_api_token = your-api-token
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

# Получаем сертификат
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com \
-d *.example.com \
--email admin@example.com \
--agree-tos \
--no-eff-email

Автообновление сертификатов

Проверка автообновления

sudo certbot renew --dry-run

Настройка cron

sudo crontab -e

Добавляем:

0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"

Или через systemd timer

Создаем /etc/systemd/system/certbot-renewal.service:

[Unit]
Description=Certbot Renewal
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet
ExecStartPost=/bin/systemctl reload nginx.service

Создаем /etc/systemd/system/certbot-renewal.timer:

[Unit]
Description=Timer for Certbot Renewal
Requires=certbot-renewal.service

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=3600

[Install]
WantedBy=timers.target
sudo systemctl enable certbot-renewal.timer
sudo systemctl start certbot-renewal.timer
sudo systemctl list-timers | grep certbot

Проверка SSL

SSL тест

# Проверка конфигурации
sudo nginx -t

# Проверка SSL
openssl s_client -connect example.com:443 -servername example.com

# Тест SSL Labs (онлайн)
# https://www.ssllabs.com/ssltest/

Скрипт проверки SSL

#!/bin/bash
# ssl-check.sh

DOMAIN=$1
if [ -z "$DOMAIN" ]; then
echo "Usage: $0 domain.com"
exit 1
fi

echo "Checking SSL for $DOMAIN..."

# Проверка срока действия
echo "=== Certificate Expiry ==="
echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | \
openssl x509 -noout -dates

# Проверка цепочки
echo "=== Certificate Chain ==="
echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 -showcerts 2>/dev/null | \
grep -E "subject=|issuer="

# Проверка TLS версий
echo "=== Supported TLS Versions ==="
for version in ssl2 ssl3 tls1 tls1_1 tls1_2 tls1_3; do
result=$(echo | timeout 3 openssl s_client -$version -connect $DOMAIN:443 2>&1)
if echo "$result" | grep -q "Cipher is"; then
echo "$version: SUPPORTED"
else
echo "$version: NOT SUPPORTED"
fi
done
chmod +x ssl-check.sh
./ssl-check.sh example.com