1. Контроль набора модулей (уменьшаем площадь атаки)
Каждый загруженный модуль — это новый потенциальный вектор атаки (уязвимость в самом модуле, его неправильная настройка). Чем меньше модулей — тем меньше дыр.
Исторически «стреляли» mod_status, mod_autoindex, CGI и прочие. При лишних модулях можно: узнать внутренности сервера, обойти авторизацию, получить RCE в цепочке с уязвимым приложением/интерпретатором, или просто расширить поверхность DoS.
Принцип минимизации доверенной базы (TCB). Чем меньше кода выполняется с правами веб-сервера, тем меньше вероятность уязвимости и тем короче цепочка эксплуатации.
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule actions_module modules/mod_actions.so
LoadModule alias_module modules/mod_alias.so
LoadModule allowmethods_module modules/mod_allowmethods.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule include_module modules/mod_include.so
LoadModule isapi_module modules/mod_isapi.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule mime_module modules/mod_mime.so
LoadModule negotiation_module modules/mod_negotiation.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.soДержите список кратким. Всё, что не нужно — выкинуть. «Вдруг пригодится» — плохая стратегия в безопасности.
2. Политика «запрещено по умолчанию» на корне
Закрываем всё, затем точечно открываем нужные каталоги.
Незаметные каталоги/файлы (резервные копии, временные, забытые dev-папки) могут внезапно стать доступными анонимно.
Default-deny > Default-allow. Ошибиться, добавив лишний Require all granted, проще, чем случайно забыть закрыть то, что не должно светиться.
<Directory />
AllowOverride none
Require all denied
</Directory>Сначала запрещаем всё, затем даём ровно столько доступа, сколько нужно сайту.
3. Твёрдая конфигурация каталога сайта: индексы, .ht*, управляемые доступы
Отключаем автолистинг, «перекрываем» .ht*, и не позволяем .htaccess портить нашу политику.
Options Indexes→ утечка структуры проекта и статических файлов.- Разрешённый
.htaccess→ локальный RCE/обход правил через коммит одного файла. - Скрытые/служебные файлы → прямое чтение секретов.
.htaccess — удобен, но небезопасен: разброс политик, медленнее I/O, выше риск конфиг-инъекций при компрометации деплоя.
<Directory "${SRVROOT}/htdocs">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
<FilesMatch "^\.(?!well-known/).+">
Require all denied
</FilesMatch>
<FilesMatch "\.(env|ini|cfg|conf|bak|swp)$">
Require all denied
</FilesMatch>
</Directory>
<Files ".ht*">
Require all denied
</Files>Закрываем индексы, запрещаем .htaccess и «невидимки», оставляем только то, что реально нужно пользователю.
4. Полный запрет на CGI
CGI — классика уязвимостей: исполнение внешних интерпретаторов по HTTP—запросу.
CGI скрипт с багом == RCE. Даже если вы не используете CGI, оставленный «на всякий случай» каталог — РИСК.
Принцип «отключить интерпретаторы в вебе» максимально упрощает модель угроз.
<Directory "${SRVROOT}/cgi-bin">
AllowOverride None
Options None
Require all denied
</Directory>
<LocationMatch "^/cgi-bin/?">
Require all denied
</LocationMatch>Нет CGI — нет класса атак, завязанных на него.
5. Минимизируем утечки метаданных и векторы трассировки
Скрываем версию, отключаем TRACE, не даём подсказок сканерам.
Банальный баннер-граббинг подбирает эксплойт под вашу версию. TRACE помогает в XST и трюках с заголовками.
Security by configuration: не даём нападающему лишнюю телеметрию, убираем редко нужные методы.
TraceEnable off
ServerTokens Prod
ServerSignature Off
HostnameLookups OffЧем меньше о вас знает сканер — тем сложнее прицелиться.
6. Жёсткие заголовки безопасности (XFO, XCTO, XSSP, Referrer, Permissions)
Ограничиваем встраивание, MIME-сниффинг, реферы и доступ к устройствам.
- Clickjacking через
<iframe>безX-Frame-Options. - MIME-сниффинг → загрузка JS как «текста» превращается в исполняемый скрипт.
- Лишние реферы → утечка токенов/URL с секретами.
- Permissions-Policy → ограничиваем потенциальные злоупотребления API.
Безопасность фронтенда — это политика браузера. Заголовки — ваш компактный CSP-лайт.
<IfModule headers_module>
RequestHeader unset Proxy early
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>Добавьте ещё полноценную CSP в продакшене. X-XSS-Protection устарел, но вреда не делает — реальную пользу даёт CSP.
7. Блокирование кодированных обходов путей
Запрещаем декодирование закодированных слешей и отлавливаем попытки traversal/эксплойтов через подозрительные URI.
%2f, %5c, двойная/тройная кодировка и «..» могут пробить проверку маршрутов и вытащить файлы за пределами DocumentRoot или попасть в неожиданный location.
URI-нормализация — боль. Проще явно пресечь нестандартные формы и двойные кодировки.
AllowEncodedSlashes NoDecode
SetEnvIfNoCase Request_URI "(?i)(?:/cgi-bin\b|/bin/sh\b)" BAD_URI=1
SetEnvIfNoCase Request_URI "(?i)(?:\.\.|%2e%2e|%25%32%65%25%32%65|%252e%252e|%2f%2e%2e|%5c%2e%2e)" BAD_URI=1
SetEnvIfNoCase Request_URI "(?i)(?:%2f|%5c)" BAD_URI=1
<Location "/">
<RequireAll>
Require all granted
Require not env BAD_URI
</RequireAll>
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
</Location>Это легковесный «WAF-лайт». Он не заменяет полноценный WAF, но отрубит массу мусорных и опасных запросов.
8. Белый список методов
Ограничиваем методы до безопасного минимума (чтение/простые формы).
Методы вроде PUT, DELETE, OPTIONS, PROPFIND и экзотика могут неожиданно «сработать» через модуль/баг приложения и дать запись/удаление.
Least functionality: включаем только то, что реально нужно маршрутам.
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>Если приложению понадобится, вы знаете, где открыть точечно, а не «везде».
9. Ограничение размеров запросов (анти-DoS/анти-smuggling)
Защищаем парсер заголовков и апстрим от огромных/грязных запросов.
Большие заголовки/количество полей — классический low-rate DoS. Также меньше шансов на хитрые атаки по переполнению/смещению (HTTP Request Smuggling любит нестандартные заголовки).
Rate-limiting != size-limiting. Нужны оба. Здесь — ограничение по размеру.
LimitRequestLine 8190
LimitRequestFieldSize 8190
LimitRequestFields 100Значения разумные для большинства фреймворков. Если у вас нестандартные большие JWT в куках — пересмотрите дизайн, а не границы.
10. TLS: сессии и OCSP stapling
Стабильные и быстрые TLS-сессии и проверка отзыва сертификатов (stapling).
Технически пробиться сложнее, но отсутствие stapling → клиенты сами ходят к OCSP-серверу (утечки, нестабильность). Плюс без кэша сессий можно легче задушить CPU пиками TLS-рукопожатий.
OCSP stapling — сервер прикладывает свежий статус сертификата к рукопожатию; кэши — снижают стоимость повторных соединений.
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLSessionCache "shmcb:${SRVROOT}/logs/ssl_scache(512000)"
SSLSessionCacheTimeout 300
SSLUseStapling On
SSLStaplingCache "shmcb:${SRVROOT}/logs/stapling_cache(128000)"
</IfModule>Это базовый гигиенический минимум TLS.
11. Принудительный переход на HTTPS
Любой HTTP-запрос — редирект на HTTPS. Убираем смешанный трафик.
MitM, перехват куки, подмена контента, down-grade атаки. HTTP — открытка без конверта.
HTTPS-везде + HSTS — стандарт индустрии. Сначала редирект, затем HSTS (с preload — по ситуации).
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
ErrorLog "logs/redirect_error.log"
CustomLog "logs/redirect_access.log" combined
</VirtualHost>Далее еще разберемся с этим подробнее.
12. Логи как средство безопасности (детект и разбор инцидентов)
Без логов вы слепы: не увидите скан, перебор, попытки LFI/RCE, неожиданные методы.
Атакующий уходит «в тень»: без корректного формата и раздельных логов расследование затянется или станет невозможным.
Observability — часть безопасности. Логи доступа + ошибок, отдельные файлы под редиректы/SSL/приложение.
ErrorLog "logs/error.log"
LogLevel warn
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
CustomLog "logs/access.log" combined
</IfModule>Лично я периодически скармливаю логи ИИ для проверки. Да, костыль, в будущем автоматизирую.
13. Анти-прокси-мусор в запросах
Сносим подозрительный заголовок Proxy на ранней стадии — бывает полезно против некоторых кривых прокси-цепочек/багов.
Некоторые уязвимости на границах прокси/бэкендов вспыхивают от неожиданных служебных заголовков в сыром виде.
Гигиена заголовков: всё, что не нужно приложению, лучше очистить/зарезервировать.
RequestHeader unset Proxy earlyМелочь, а приятно. Особенно в зоопарке из CDN, слойных прокси и бэкендов.
Итого
Вот такой код должен +- получиться:
Define SRVROOT "C:/Apache24"
ServerRoot "${SRVROOT}"
Listen 80
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule actions_module modules/mod_actions.so
LoadModule alias_module modules/mod_alias.so
LoadModule allowmethods_module modules/mod_allowmethods.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule include_module modules/mod_include.so
LoadModule isapi_module modules/mod_isapi.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule mime_module modules/mod_mime.so
LoadModule negotiation_module modules/mod_negotiation.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
LoadFile "C:/Program Files/Python313/python313.dll"
LoadModule wsgi_module "C:/Program Files/Python313/Lib/site-packages/mod_wsgi/server/mod_wsgi.cp313-win_amd64.pyd"
WSGIPythonHome "C:/Apache24/htdocs/venv"
WSGIPythonPath "C:/Apache24/htdocs/our_project"
<IfModule unixd_module>
User daemon
Group daemon
</IfModule>
ServerAdmin admin@example.com
ServerName example.com
<Directory />
AllowOverride none
Require all denied
</Directory>
DocumentRoot "${SRVROOT}/htdocs"
<Directory "${SRVROOT}/htdocs">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
<FilesMatch "^\.(?!well-known/).+">
Require all denied
</FilesMatch>
<FilesMatch "\.(env|ini|cfg|conf|bak|swp)$">
Require all denied
</FilesMatch>
</Directory>
<IfModule dir_module>
DirectoryIndex index.html
</IfModule>
<Files ".ht*">
Require all denied
</Files>
ErrorLog "logs/error.log"
LogLevel warn
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
CustomLog "logs/access.log" combined
</IfModule>
<Directory "${SRVROOT}/cgi-bin">
AllowOverride None
Options None
Require all denied
</Directory>
<LocationMatch "^/cgi-bin/?">
Require all denied
</LocationMatch>
<IfModule mime_module>
TypesConfig conf/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
</IfModule>
<IfModule proxy_html_module>
Include conf/extra/proxy-html.conf
</IfModule>
TraceEnable off
ServerTokens Prod
ServerSignature Off
HostnameLookups Off
<IfModule headers_module>
RequestHeader unset Proxy early
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>
AllowEncodedSlashes NoDecode
SetEnvIfNoCase Request_URI "(?i)(?:/cgi-bin\b|/bin/sh\b)" BAD_URI=1
SetEnvIfNoCase Request_URI "(?i)(?:\.\.|%2e%2e|%25%32%65%25%32%65|%252e%252e|%2f%2e%2e|%5c%2e%2e)" BAD_URI=1
SetEnvIfNoCase Request_URI "(?i)(?:%2f|%5c)" BAD_URI=1
<Location "/">
<RequireAll>
Require all granted
Require not env BAD_URI
</RequireAll>
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
</Location>
LimitRequestLine 8190
LimitRequestFieldSize 8190
LimitRequestFields 100
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLSessionCache "shmcb:${SRVROOT}/logs/ssl_scache(512000)"
SSLSessionCacheTimeout 300
SSLUseStapling On
SSLStaplingCache "shmcb:${SRVROOT}/logs/stapling_cache(128000)"
</IfModule>
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
ErrorLog "logs/redirect_error.log"
CustomLog "logs/redirect_access.log" combined
</VirtualHost>
Include conf/extra/httpd-ssl.conf