1. Переход на шифрованный порт
Cлушаем только 443 для защищённого трафика. Это убирает соблазн держать функционал на нешифрованном HTTP и сокращает поверхность атак типа MitM.
Как ломают без этого: перехват и подмена трафика, кража cookies/сессий, даунгрейд до HTTP.
Listen 443
Базовая гигиена — всё чувствительное только по TLS.
2. Изолированный виртуал по умолчанию
_default_:443 гарантирует, что любой хост, пришедший на 443 без явного VirtualHost, попадёт в безопасный профиль.
<VirtualHost _default_:443>
ServerAdmin admin@example.com
ServerName example.com
DocumentRoot "C:/Apache24/htdocs"
#</VirtualHost> будет в конце
3. Включение TLS и корректные сертификаты
Включаем TLS и указываем цепочку/ключ. Правильные файлы и права доступа к ним — основа доверия.
Как ломают без этого: самоподписанные/битые цепочки → фишинг через предупреждения; утечка приватного ключа → полная компрометация.
SSLEngine on
SSLCertificateFile "C:/Apache24/conf/ssl/domain.cert.pem"
SSLCertificateKeyFile "C:/Apache24/conf/ssl/private.key.pem"
Держите ключи вне web-root, с правами только для пользователя службы.
4. Политика протоколов и шифров
Отключаем устаревшие протоколы и слабые шифры, навязываем порядок выбора сервером.
Как ломают без этого: даунгрейд до TLS 1.0/1.1, атаки на CBC/3DES/RC4/MD5, BEAST/Lucky13 и пр.
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
SSLHonorCipherOrder On
SSLCompression Off
Итого: Минимум — TLS 1.2/1.3, запрет компрессии (CRIME) и мусорных шифров.
5. Проверка статуса сертификата (OCSP stapling)
Сервер сам прикладывает свежий статус сертификата. Клиентам не нужно ходить к OCSP-серверу.
Без этого приватность хуже (утечки запросов к OCSP), нестабильность проверки => пользователи игнорируют предупреждения.
SSLUseStapling On
Stapling повышает и приватность, и производительность.
6. Принудительный HSTS
Заставляем браузер использовать только HTTPS, включая поддомены, и разрешаем preload.
Как ломают без этого: даунгрейд до HTTP, перехват куки, смешанный контент.
<IfModule headers_module>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</IfModule>
Вывод: включайте HSTS только после того, как весь домен реально готов жить на HTTPS.
7. Точка входа WSGI с минимальными правами
Явное сопоставление корня к WSGI-приложению и запрет листинга.
Как ломают без этого: случайные статики/служебные файлы оказываются доступны, обход роутинга до файлов.
WSGIScriptAlias / "C:/Apache24/htdocs/our_project/our_project/wsgi.py"
<Directory "C:/Apache24/htdocs/our_project/our_project/">
<Files wsgi.py>
Require all granted
Options -Indexes
</Files>
</Directory>
8. Статика: открываем, но без индексов
Раздаём статические файлы без автолистинга, чтобы не светить структуру проекта.
Alias /static "C:/Apache24/htdocs/our_project/staticfiles/"
<Directory "C:/Apache24/htdocs/our_project/staticfiles/">
Require all granted
Options -Indexes
</Directory>
Вывод: видно — только то, что явно адресовано
9. Медиа: запрет опасных расширений + кэш
Раздаём пользовательские файлы, но блокируем всё, что может исполняться.
Как ломают без этого: загрузка и запуск .py/.exe/.ps1 и др. → RCE/привилегии, хранение payload’ов на домене.
Alias /media/ "C:/Apache24/htdocs/our_project/media/"
<Directory "C:/Apache24/htdocs/our_project/media/">
Require all granted
Options -Indexes
<FilesMatch "\.(py|pyc|pyo|sh|bat|cmd|ps1|exe)$">
Require all denied
</FilesMatch>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 7 days"
</IfModule>
</Directory>
Медиа — это данные, а не код. Запрещаем исполняемые форматы и задаём безопасный TTL.
10. Ограничение размера тела запроса
Ставим ограничение против перегрузок бэкенда и злоупотреблений загрузками.
Предотвращаем DoS большими телами, попытки пронести огромные архивы/словарные атаки по upload.
LimitRequestBody 26214400 # 25 MiB
Подстраивайте лимит под реальные нужды; лучше меньше, чем «как-нибудь потом».
11. Раздельные логи для TLS-виртуала
Отдельные файлы ошибок/доступа упрощают расследование инцидентов по HTTPS.
Как ломают без этого: шум общей ротации скрывает аномалии, теряется контекст.
ErrorLog "logs/ssl_error.log"
CustomLog "logs/ssl_access.log" combined
</VirtualHost>
Лично я скармливаю логи ИИ для анализа. Как и в прошлой статье, я признаюсь, это костыль. Но, видимо, пока меня не взломают, я не займусь автоматизацией.
Итого
+- Вот так должно выглядеть у вас:
Listen 443
ServerAdmin admin@example.com
ServerName example.com
DocumentRoot "C:/Apache24/htdocs"
SSLEngine on
SSLCertificateFile "C:/Apache24/conf/ssl/domain.cert.pem"
SSLCertificateKeyFile "C:/Apache24/conf/ssl/private.key.pem"
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
SSLHonorCipherOrder On
SSLCompression Off
SSLUseStapling On
<IfModule headers_module>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</IfModule>
WSGIScriptAlias / "C:/Apache24/htdocs/our_project/our_project/wsgi.py"
<Directory "C:/Apache24/htdocs/our_project/our_project/">
<Files wsgi.py>
Require all granted
Options -Indexes
</Files>
</Directory>
Alias /static "C:/Apache24/htdocs/our_project/staticfiles/"
<Directory "C:/Apache24/htdocs/our_project/staticfiles/">
Require all granted
Options -Indexes
</Directory>
Alias /media/ "C:/Apache24/htdocs/our_project/media/"
<Directory "C:/Apache24/htdocs/our_project/media/">
Require all granted
Options -Indexes
<FilesMatch "\.(py|pyc|pyo|sh|bat|cmd|ps1|exe)$">
Require all denied
</FilesMatch>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 7 days"
</IfModule>
</Directory>
LimitRequestBody 26214400
ErrorLog "logs/ssl_error.log"
CustomLog "logs/ssl_access.log" combined