HTTPS в Django
Настройка HTTPS обеспечивает безопасность вашего Django приложения, защищая передачу данных между клиентом и сервером. Это критически важно для защиты пользовательских данных и соответствия современным стандартам безопасности.
Зачем нужен HTTPS
HTTPS обеспечивает:
- Шифрование данных между клиентом и сервером
- Аутентификацию сервера (защита от атак "человек посередине")
- Целостность данных (защита от подмены)
- Соответствие требованиям GDPR и других регуляторов
- Лучшие позиции в поисковых системах
- Доверие пользователей
Получение SSL сертификата
1. Let's Encrypt (бесплатно)
1# Установка Certbot
2sudo apt update
3sudo apt install certbot python3-certbot-nginx
4
5# Получение сертификата для домена
6sudo certbot --nginx -d example.com -d www.example.com
7
8# Автоматическое обновление
9sudo crontab -e
10# Добавляем строку для автоматического обновления
110 12 * * * /usr/bin/certbot renew --quiet
2. Коммерческие сертификаты
3. Самоподписанный сертификат (для разработки)
Настройка Django для HTTPS
1. Основные настройки безопасности
1# settings.py
2
3# HTTPS настройки
4SECURE_SSL_REDIRECT = True # Редирект HTTP на HTTPS
5SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
6
7# HSTS (HTTP Strict Transport Security)
8SECURE_HSTS_SECONDS = 31536000 # 1 год
9SECURE_HSTS_INCLUDE_SUBDOMAINS = True
10SECURE_HSTS_PRELOAD = True
11
12# Дополнительные настройки безопасности
13SECURE_CONTENT_TYPE_NOSNIFF = True
14SECURE_BROWSER_XSS_FILTER = True
15SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin'
16
17# Настройки сессий и куки
18SESSION_COOKIE_SECURE = True
19CSRF_COOKIE_SECURE = True
20CSRF_COOKIE_HTTPONLY = True
21SESSION_COOKIE_HTTPONLY = True
22SESSION_COOKIE_SAMESITE = 'Lax'
23
24# CSP (Content Security Policy)
25CSP_DEFAULT_SRC = ("'self'",)
26CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
27CSP_SCRIPT_SRC = ("'self'",)
28CSP_IMG_SRC = ("'self'", "data:", "https:")
29
30# Настройки для продакшена
31DEBUG = False
32ALLOWED_HOSTS = ['example.com', 'www.example.com']
2. Условные настройки для разных окружений
1# settings/base.py
2import os
3
4# Базовые настройки
5SECURE_SSL_REDIRECT = False # По умолчанию отключено
6SECURE_HSTS_SECONDS = 0
7
8# settings/production.py
9from .base import *
10
11# Продакшен настройки
12SECURE_SSL_REDIRECT = True
13SECURE_HSTS_SECONDS = 31536000
14SECURE_HSTS_INCLUDE_SUBDOMAINS = True
15SECURE_HSTS_PRELOAD = True
16
17# Настройки для прокси (если используешь Nginx/Apache)
18SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
19
20# settings/staging.py
21from .base import *
22
23# Staging настройки (промежуточные)
24SECURE_SSL_REDIRECT = True
25SECURE_HSTS_SECONDS = 3600 # 1 час для тестирования
26SECURE_HSTS_INCLUDE_SUBDOMAINS = False
27SECURE_HSTS_PRELOAD = False
3. Настройки для Docker
1# settings/docker.py
2import os
3
4# Настройки для Docker контейнеров
5SECURE_SSL_REDIRECT = os.environ.get('SECURE_SSL_REDIRECT', 'False').lower() == 'true'
6SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
7
8# Настройки для reverse proxy
9USE_X_FORWARDED_HOST = True
10USE_X_FORWARDED_PORT = True
11
12# Настройки для Cloudflare
13SECURE_PROXY_SSL_HEADER = ('HTTP_CF_VISITOR', '{"scheme":"https"}')
Настройка веб-сервера
1. Nginx конфигурация
1# /etc/nginx/sites-available/example.com
2server {
3 listen 80;
4 server_name example.com www.example.com;
5
6 # Редирект HTTP на HTTPS
7 return 301 https://$server_name$request_uri;
8}
9
10server {
11 listen 443 ssl http2;
12 server_name example.com www.example.com;
13
14 # SSL сертификаты
15 ssl_certificate /etc/ssl/certs/example.com.crt;
16 ssl_certificate_key /etc/ssl/private/example.com.key;
17
18 # SSL настройки
19 ssl_protocols TLSv1.2 TLSv1.3;
20 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
21 ssl_prefer_server_ciphers off;
22 ssl_session_cache shared:SSL:10m;
23 ssl_session_timeout 10m;
24
25 # HSTS заголовки
26 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
27
28 # Дополнительные заголовки безопасности
29 add_header X-Frame-Options DENY;
30 add_header X-Content-Type-Options nosniff;
31 add_header X-XSS-Protection "1; mode=block";
32 add_header Referrer-Policy "strict-origin-when-cross-origin";
33
34 # Проксирование к Django
35 location / {
36 proxy_pass http://127.0.0.1:8000;
37 proxy_set_header Host $host;
38 proxy_set_header X-Real-IP $remote_addr;
39 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
40 proxy_set_header X-Forwarded-Proto $scheme;
41 }
42
43 # Статические файлы
44 location /static/ {
45 alias /path/to/your/staticfiles/;
46 expires 1y;
47 add_header Cache-Control "public, immutable";
48 }
49
50 location /media/ {
51 alias /path/to/your/media/;
52 expires 1y;
53 add_header Cache-Control "public";
54 }
55}
2. Apache конфигурация
1# /etc/apache2/sites-available/example.com.conf
2<VirtualHost *:80>
3 ServerName example.com
4 ServerAlias www.example.com
5
6 # Редирект на HTTPS
7 Redirect permanent / https://example.com/
8</VirtualHost>
9
10<VirtualHost *:443>
11 ServerName example.com
12 ServerAlias www.example.com
13
14 # SSL настройки
15 SSLEngine on
16 SSLCertificateFile /etc/ssl/certs/example.com.crt
17 SSLCertificateKeyFile /etc/ssl/private/example.com.key
18
19 # Дополнительные SSL настройки
20 SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
21 SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384
22
23 # HSTS заголовки
24 Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
25
26 # Дополнительные заголовки безопасности
27 Header always set X-Frame-Options DENY
28 Header always set X-Content-Type-Options nosniff
29 Header always set X-XSS-Protection "1; mode=block"
30 Header always set Referrer-Policy "strict-origin-when-cross-origin"
31
32 # Проксирование к Django
33 ProxyPreserveHost On
34 ProxyPass / http://127.0.0.1:8000/
35 ProxyPassReverse / http://127.0.0.1:8000/
36
37 # Статические файлы
38 Alias /static/ /path/to/your/staticfiles/
39 <Directory /path/to/your/staticfiles/>
40 Require all granted
41 ExpiresActive On
42 ExpiresDefault "access plus 1 year"
43 </Directory>
44</VirtualHost>
3. Gunicorn с SSL
1# gunicorn_config.py
2import multiprocessing
3
4# Основные настройки
5bind = "0.0.0.0:8000"
6workers = multiprocessing.cpu_count() * 2 + 1
7worker_class = "gevent"
8worker_connections = 1000
9
10# SSL настройки (если нужно запускать с SSL напрямую)
11# keyfile = "/path/to/private.key"
12# certfile = "/path/to/certificate.crt"
13
14# Настройки безопасности
15limit_request_line = 4094
16limit_request_fields = 100
17limit_request_field_size = 8190
18
19# Логирование
20accesslog = "/var/log/gunicorn/access.log"
21errorlog = "/var/log/gunicorn/error.log"
22loglevel = "info"
23
24# Настройки для прокси
25forwarded_allow_ips = "127.0.0.1,::1"
26proxy_allow_forwarded = True
Middleware для безопасности
1. Кастомный HTTPS middleware
1# middleware.py
2from django.http import HttpResponsePermanentRedirect
3from django.conf import settings
4
5class HTTPSRedirectMiddleware:
6 """Middleware для принудительного редиректа на HTTPS"""
7
8 def __init__(self, get_response):
9 self.get_response = get_response
10
11 def __call__(self, request):
12 # Проверяем, нужно ли принудительно перенаправлять на HTTPS
13 if getattr(settings, 'SECURE_SSL_REDIRECT', False):
14 # Проверяем заголовки прокси
15 if getattr(settings, 'SECURE_PROXY_SSL_HEADER', None):
16 header_name, header_value = settings.SECURE_PROXY_SSL_HEADER
17 if request.META.get(header_name) != header_value:
18 # Перенаправляем на HTTPS
19 url = request.build_absolute_uri(request.get_full_path())
20 if url.startswith('http://'):
21 url = 'https://' + url[7:]
22 return HttpResponsePermanentRedirect(url)
23
24 response = self.get_response(request)
25 return response
26
27class SecurityHeadersMiddleware:
28 """Middleware для добавления заголовков безопасности"""
29
30 def __init__(self, get_response):
31 self.get_response = get_response
32
33 def __call__(self, request):
34 response = self.get_response(request)
35
36 # Добавляем заголовки безопасности
37 response['X-Frame-Options'] = 'DENY'
38 response['X-Content-Type-Options'] = 'nosniff'
39 response['X-XSS-Protection'] = '1; mode=block'
40 response['Referrer-Policy'] = 'strict-origin-when-cross-origin'
41
42 # HSTS заголовок
43 if getattr(settings, 'SECURE_HSTS_SECONDS', 0) > 0:
44 hsts_value = f"max-age={settings.SECURE_HSTS_SECONDS}"
45 if getattr(settings, 'SECURE_HSTS_INCLUDE_SUBDOMAINS', False):
46 hsts_value += "; includeSubDomains"
47 if getattr(settings, 'SECURE_HSTS_PRELOAD', False):
48 hsts_value += "; preload"
49 response['Strict-Transport-Security'] = hsts_value
50
51 return response
2. Настройка middleware в settings
1# settings.py
2MIDDLEWARE = [
3 'django.middleware.security.SecurityMiddleware',
4 'django.contrib.sessions.middleware.SessionMiddleware',
5 'django.middleware.common.CommonMiddleware',
6 'django.middleware.csrf.CsrfViewMiddleware',
7 'django.contrib.auth.middleware.AuthenticationMiddleware',
8 'django.contrib.messages.middleware.MessageMiddleware',
9 'django.middleware.clickjacking.XFrameOptionsMiddleware',
10
11 # Кастомные middleware
12 'your_app.middleware.HTTPSRedirectMiddleware',
13 'your_app.middleware.SecurityHeadersMiddleware',
14]
Практические примеры
1. Настройка для разных доменов
1# settings.py
2import socket
3
4# Определяем текущий хост
5hostname = socket.gethostname()
6
7# Настройки для разных окружений
8if hostname.startswith('prod-'):
9 # Продакшен
10 SECURE_SSL_REDIRECT = True
11 SECURE_HSTS_SECONDS = 31536000
12 SECURE_HSTS_INCLUDE_SUBDOMAINS = True
13 SECURE_HSTS_PRELOAD = True
14 ALLOWED_HOSTS = ['example.com', 'www.example.com']
15
16elif hostname.startswith('staging-'):
17 # Staging
18 SECURE_SSL_REDIRECT = True
19 SECURE_HSTS_SECONDS = 3600
20 SECURE_HSTS_INCLUDE_SUBDOMAINS = False
21 SECURE_HSTS_PRELOAD = False
22 ALLOWED_HOSTS = ['staging.example.com']
23
24else:
25 # Разработка
26 SECURE_SSL_REDIRECT = False
27 SECURE_HSTS_SECONDS = 0
28 ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'dev.example.com']
2. Настройка для Cloudflare
1# settings.py
2
3# Настройки для Cloudflare
4if 'HTTP_CF_VISITOR' in request.META:
5 # Определяем протокол из заголовка Cloudflare
6 import json
7 cf_visitor = json.loads(request.META['HTTP_CF_VISITOR'])
8 is_https = cf_visitor.get('scheme') == 'https'
9
10 if is_https:
11 SECURE_PROXY_SSL_HEADER = ('HTTP_CF_VISITOR', '{"scheme":"https"}')
12 SECURE_SSL_REDIRECT = True
13
14# Доверенные IP адреса Cloudflare
15SECURE_PROXY_SSL_HEADER = ('HTTP_CF_CONNECTING_IP', 'https')
16SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
17
18# Настройки для Cloudflare
19USE_X_FORWARDED_HOST = True
20USE_X_FORWARDED_PORT = True
3. Настройка для Heroku
1# settings.py
2
3# Настройки для Heroku
4if 'DYNO' in os.environ:
5 # Heroku environment
6 SECURE_SSL_REDIRECT = True
7 SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
8
9 # Настройки для Heroku
10 USE_X_FORWARDED_HOST = True
11 USE_X_FORWARDED_PORT = True
12
13 # HSTS настройки
14 SECURE_HSTS_SECONDS = 31536000
15 SECURE_HSTS_INCLUDE_SUBDOMAINS = True
16 SECURE_HSTS_PRELOAD = True
Тестирование HTTPS
1. Проверка SSL сертификата
1# Проверка сертификата
2openssl s_client -connect example.com:443 -servername example.com
3
4# Проверка цепочки сертификатов
5openssl s_client -connect example.com:443 -servername example.com -showcerts
6
7# Проверка через curl
8curl -I https://example.com
9
10# Проверка заголовков безопасности
11curl -I -H "User-Agent: Mozilla/5.0" https://example.com
2. Онлайн инструменты для проверки
1# Скрипт для проверки безопасности
2import requests
3import ssl
4import socket
5
6def check_https_security(domain):
7 """Проверка безопасности HTTPS"""
8 results = {}
9
10 try:
11 # Проверяем SSL сертификат
12 context = ssl.create_default_context()
13 with socket.create_connection((domain, 443)) as sock:
14 with context.wrap_socket(sock, server_hostname=domain) as ssock:
15 cert = ssock.getpeercert()
16 results['ssl_valid'] = True
17 results['ssl_expires'] = cert['notAfter']
18 results['ssl_issuer'] = cert['issuer']
19 except Exception as e:
20 results['ssl_valid'] = False
21 results['ssl_error'] = str(e)
22
23 try:
24 # Проверяем заголовки безопасности
25 response = requests.get(f'https://{domain}', timeout=10)
26 results['https_working'] = True
27
28 # Проверяем HSTS
29 hsts_header = response.headers.get('Strict-Transport-Security')
30 results['hsts_enabled'] = hsts_header is not None
31
32 # Проверяем другие заголовки
33 results['x_frame_options'] = response.headers.get('X-Frame-Options')
34 results['x_content_type_options'] = response.headers.get('X-Content-Type-Options')
35 results['x_xss_protection'] = response.headers.get('X-XSS-Protection')
36
37 except Exception as e:
38 results['https_working'] = False
39 results['https_error'] = str(e)
40
41 return results
42
43# Использование
44security_info = check_https_security('example.com')
45print(security_info)
Автоматизация и мониторинг
1. Автоматическое обновление сертификатов
1#!/bin/bash
2# /usr/local/bin/renew_ssl.sh
3
4# Обновление SSL сертификатов
5certbot renew --quiet
6
7# Перезагрузка Nginx
8if systemctl is-active --quiet nginx; then
9 systemctl reload nginx
10fi
11
12# Проверка статуса
13if [ $? -eq 0 ]; then
14 echo "SSL certificates renewed successfully"
15else
16 echo "SSL renewal failed"
17 # Отправка уведомления
18 curl -X POST "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK" \
19 -H "Content-type: application/json" \
20 -d '{"text":"SSL renewal failed for example.com"}'
21fi
2. Мониторинг срока действия сертификатов
1# management/commands/check_ssl_expiry.py
2from django.core.management.base import BaseCommand
3from django.core.mail import send_mail
4from django.conf import settings
5import ssl
6import socket
7from datetime import datetime, timedelta
8
9class Command(BaseCommand):
10 help = 'Check SSL certificate expiry dates'
11
12 def add_arguments(self, parser):
13 parser.add_argument(
14 '--warning-days',
15 type=int,
16 default=30,
17 help='Days before expiry to send warning'
18 )
19
20 def handle(self, *args, **options):
21 warning_days = options['warning_days']
22 domains = getattr(settings, 'SSL_DOMAINS', ['example.com'])
23
24 for domain in domains:
25 try:
26 # Получаем информацию о сертификате
27 context = ssl.create_default_context()
28 with socket.create_connection((domain, 443)) as sock:
29 with context.wrap_socket(sock, server_hostname=domain) as ssock:
30 cert = ssock.getpeercert()
31
32 # Проверяем срок действия
33 expiry_date = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
34 days_until_expiry = (expiry_date - datetime.now()).days
35
36 if days_until_expiry <= warning_days:
37 # Отправляем предупреждение
38 subject = f'SSL Certificate Expiry Warning: {domain}'
39 message = f'''
40 SSL certificate for {domain} will expire in {days_until_expiry} days.
41 Expiry date: {expiry_date}
42 Please renew the certificate.
43 '''
44
45 send_mail(
46 subject=subject,
47 message=message,
48 from_email=settings.DEFAULT_FROM_EMAIL,
49 recipient_list=settings.ADMINS,
50 fail_silently=False,
51 )
52
53 self.stdout.write(
54 self.style.WARNING(
55 f'Warning sent for {domain}: expires in {days_until_expiry} days'
56 )
57 )
58 else:
59 self.stdout.write(
60 self.style.SUCCESS(
61 f'{domain}: expires in {days_until_expiry} days'
62 )
63 )
64
65 except Exception as e:
66 self.stdout.write(
67 self.style.ERROR(f'Error checking {domain}: {e}')
68 )
FAQ
Q: Как получить SSL сертификат?
A: Используй Let's Encrypt для бесплатных сертификатов, купи у провайдера для коммерческих проектов, или создай самоподписанный для разработки.
Q: Как настроить автоматический редирект с HTTP на HTTPS?
A: Установи SECURE_SSL_REDIRECT = True в Django settings и настрой редирект в веб-сервере (Nginx/Apache) для лучшей производительности.
Q: Что такое HSTS и зачем он нужен?
A: HSTS (HTTP Strict Transport Security) принудительно использует HTTPS для всех запросов к сайту, защищая от downgrade атак и перехвата трафика.
Q: Как проверить правильность настройки HTTPS?
A: Используй онлайн инструменты (SSL Labs, Security Headers), проверяй заголовки через curl, и тестируй редирект с HTTP на HTTPS.
Q: Можно ли использовать HTTPS в разработке?
A: Да, создай самоподписанный сертификат или используй mkcert для локальной разработки с валидными сертификатами.
Q: Как настроить HTTPS для нескольких доменов?
A: Используй wildcard сертификаты или отдельные сертификаты для каждого домена, настрой соответствующие конфигурации в веб-сервере.
Q: Что делать если SSL сертификат истек?
A: Немедленно обнови сертификат, настрой автоматическое обновление через cron, и мониторь сроки действия сертификатов.
Q: Как оптимизировать производительность HTTPS?
A: Используй HTTP/2, настрой SSL session cache, включи OCSP stapling, и используй современные шифры для лучшей производительности.