Безопасность для начинающих 09. Защита сайта: TLS, заголовки, права, OWASP Top 10
Note
Материал сверен с официальной документацией (OWASP, MDN, nginx, Mozilla и др.; ссылки — в конце статьи). Примеры вывода — иллюстрации: на вашей системе значения и формат могут отличаться. Перед работой на боевых системах проверьте шаги на тестовой машине.
В пятницу вечером приходит отчёт внешней проверки: «на сайте нет HSTS и Content-Security-Policy, а каталог /.git/ отвечает кодом 200». Сайт организации открыт всему интернету, на нём работает чужой код (CMS, то есть система управления сайтом, плагины, темы), а следит за ним часто «тот, кто умеет»: это витрина и одновременно входная дверь в сервер.
В статье мы приведём в порядок типовой сайт на nginx и PHP-FPM (FastCGI Process Manager, менеджер процессов PHP; подходит для Grav, WordPress и других CMS): разберём карту рисков OWASP Top 10, настроим TLS, HSTS и заголовки безопасности, разделим права, закроем служебные файлы, защитим вход и загрузку файлов.
Warning
Меняйте только свой сайт, лучше в окно обслуживания. Перед правками сохраните конфигурацию (sudo cp -a /etc/nginx /root/nginx.backup), после каждой выполняйте sudo nginx -t и только затем sudo systemctl reload nginx. Тяжелее всего откатываются HSTS и массовое изменение прав: у них отдельные предупреждения.
Что нужно знать заранее
- Linux для админа Windows 09: блоки
serverиlocation,nginx -t,reload, журналы. - HTTPS и сертификаты: как получить сертификат и продлевать его.
- Linux для админа Windows 04: владелец, группа,
chmodиchown. - Безопасность для начинающих 01 и 03: наименьшие привилегии и процесс установки патчей.
HTTP-заголовок ответа — служебная строка, которую сервер добавляет к странице; браузер читает её как указание. XSS (cross-site scripting, межсайтовый скриптинг) — когда чужой скрипт выполняется на странице вашего сайта в браузере посетителя. WAF (web application firewall) — фильтр, который проверяет запросы к сайту. Примеры — для сайта example.com на сервере lnx1 (Ubuntu Server, nginx и PHP-FPM из пакетов Ubuntu); имена учебные.
Что видит интернет: модель и карта OWASP
Сайт — цепочка звеньев, и снаружи доступно каждое: DNS, TLS, nginx, PHP-FPM с CMS, файлы, админка и формы, база данных. Для взлома хватает одного слабого звена, поэтому защиту строят слоями (Безопасность 01).
Проект OWASP (Open Worldwide Application Security Project) ведёт список десяти самых серьёзных рисков веб-приложений; актуальная редакция — OWASP Top 10:2025. Это карта категорий, а не инструкция.
| Категория OWASP Top 10:2025 | Простыми словами | Где закрываем |
|---|---|---|
| A01 Broken Access Control | пользователь может больше, чем положено; по умолчанию доступ запрещён, кроме общедоступного | админка, права |
| A02 Security Misconfiguration | небезопасные настройки: лишнее, открытые служебные файлы, нет заголовков | заголовки, права, служебные файлы |
| A03 Software Supply Chain Failures | сбой в создании или обновлении ПО: уязвимый или подменённый плагин, тема | обновления, источники плагинов |
| A04 Cryptographic Failures | данные не зашифрованы при передаче или хранении | TLS, HSTS |
| A05 Injection | недоверенный ввод выполняется интерпретатором как команда | обновления, WAF; сам код — задача разработчика |
| A06 Insecure Design | защиты нет в самом проекте, реализация её не создаст | выбор CMS и плагинов |
| A07 Authentication Failures | чужого принимают за своего: пароли по умолчанию, слабые пароли, перебор | вход, MFA, ограничение частоты |
| A08 Software or Data Integrity Failures | недоверенные код или данные считаются доверенными | обновления из официальных источников |
| A09 Security Logging & Alerting Failures | атаку не видно: нет журналов и оповещений | журналы, мониторинг |
| A10 Mishandling of Exceptional Conditions | программа плохо справляется с необычными ситуациями | ограничение частоты, журналы |
A05 и A06 решает в первую очередь тот, кто пишет код: если сайт делает подрядчик, сверяйте его работу с этим списком (практика автора). Остальное — работа администратора.
HTTPS и настройки TLS
Получение и продление сертификата описано в статье про Let's Encrypt, а ssl_protocols и редирект на HTTPS — в Linux 09. Здесь — профиль и проверка.
Руководство Mozilla по настройке TLS предлагает два профиля. Для публичного сайта подходит Intermediate: TLS 1.2 и 1.3, рассчитан и на старые клиенты (актуальный список клиентов — в генераторе). Профиль Modern оставляет только TLS 1.3 и годится, когда вы знаете, что старых клиентов нет. Готовый блок под вашу версию nginx и OpenSSL выдаёт генератор https://ssl-config.mozilla.org/ (он перенаправляет на проект сообщества, см. врезку). Берите блок целиком: кривые, шифры и кэш сессий не набирают по памяти.
# блок профиля Intermediate из генератора (в блок server с listen 443 ssl)
ssl_ecdh_curve <КРИВЫЕ_ИЗ_ГЕНЕРАТОРА>;
ssl_ciphers <ШИФРЫ_ИЗ_ГЕНЕРАТОРА>;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
Какие протоколы действуют на самом деле, покажет полная конфигурация, включая файлы сайтов из sites-enabled:
sudo nginx -T 2>/dev/null | grep -n ssl_protocols
Активные строки должны содержать только TLSv1.2 TLSv1.3. Вывод nginx -T — вся конфигурация, не публикуйте его. Затем проверьте, что клиент соединяется и по TLS 1.2, и по 1.3:
curl -sI --tlsv1.2 --tls-max 1.2 https://example.com/ | head -n 1
curl -sI --tlsv1.3 https://example.com/ | head -n 1
Каждая команда печатает строку ответа, например HTTP/2 200. Обратная проверка (--tlsv1.0 --tls-max 1.1 должна завершаться ошибкой) убедительна, только если клиент предлагает старые версии. Откат правок TLS: вернуть сохранённый файл, nginx -t, reload.
HSTS: включаем осторожно
HSTS (HTTP Strict Transport Security) — заголовок, который говорит браузеру: этот сайт открывать только по HTTPS в течение max-age секунд. Браузер запоминает это и дальше обращается к сайту только по HTTPS. Сила HSTS и его цена — в одном: он действует долго, и быстро отменить его нельзя.
Что важно знать, по описанию MDN:
- заголовок действует до конца срока, даже если вы перестали его отправлять;
- отключить можно только значением
max-age=0, и браузер получит его, лишь когда успешно подключится по HTTPS (так задумано: по небезопасному HTTP HSTS не отключить); - по HTTP заголовок игнорируется, поэтому отправляйте его только в блоке
listen 443 ssl; - при ошибке сертификата браузер не даёт посетителю «всё равно продолжить».
Просроченный сертификат сделает сайт недоступным: перед HSTS проверьте автопродление и мониторинг срока.
Поднимаем срок ступенями и выдерживаем каждую не меньше её срока: браузеры хранят полученное значение до конца срока, поэтому проблема проявится не сразу (значения и выдержка — практика автора):
| Ступень | max-age, секунд |
Примерно |
|---|---|---|
| 1 | 300 |
5 минут |
| 2 | 86400 |
сутки |
| 3 | 604800 |
неделя |
| 4 | 2592000 |
30 суток |
| 5 | 31536000 |
год |
В примерах OWASP и Mozilla стоит 63072000 (два года), но заголовок сначала нужно изучить. Если на ступени что-то сломалось, верните max-age=0 и держите HTTPS рабочим, пока не истечёт срок, запомненный браузерами.
Два необязательных параметра рискованнее самого заголовка. includeSubDomains распространяет правило на все поддомены: включайте его, только когда все они, включая забытые тестовые и внутренние имена вроде intranet.example.com, работают по HTTPS с действующим сертификатом. preload по MDN требует max-age не менее 31536000 и includeSubDomains и вносит домен в общий список браузеров через сервис Google; это не часть спецификации HSTS. Как убрать домен из списка, в источниках статьи не описано, поэтому малой организации мы preload не рекомендуем (практика автора).
Заголовки безопасности в nginx
Заголовки ответа — это указания браузеру. Они не лечат дыры в коде, но закрывают ошибки настройки: отправка защитных директив клиентам входит в меры против A02. Значения ниже — по OWASP HTTP Headers Cheat Sheet.
| Заголовок | Что делает | Начальное значение |
|---|---|---|
X-Content-Type-Options |
nosniff: браузер не «угадывает» тип файла и блокирует скрипты и стили с неверным MIME-типом (обозначением вида файла, например text/css) |
nosniff |
Referrer-Policy |
что браузер сообщает другим сайтам о странице, с которой пришёл посетитель | strict-origin-when-cross-origin |
Permissions-Policy |
отключает функции браузера, не нужные сайту | geolocation=(), camera=(), microphone=() (пример OWASP) |
X-Frame-Options |
запрещает вставлять ваши страницы в чужой <iframe> (кликджекинг) |
SAMEORIGIN |
Content-Security-Policy |
перечисляет, что странице разрешено загружать и кто может её встраивать | см. ниже |
| HSTS | см. выше | max-age=300 на первой ступени |
Задавайте Referrer-Policy явно, а не полагайтесь на умолчания браузеров, и оставляйте в Permissions-Policy только нужные сайту функции. X-Frame-Options — старый заголовок: в браузерах, которые поддерживают frame-ancestors из CSP, он не нужен, а для остальных остаётся единственной защитой. DENY запрещает любые рамки, SAMEORIGIN разрешает только страницы вашего сайта; если вы сами встраиваете свои страницы в рамки, начните с SAMEORIGIN. Заголовки Server и X-Powered-By лучше убрать или сделать безликими; в nginx server_tokens off убирает из Server номер версии (Linux 09).
Content-Security-Policy
CSP (Content Security Policy, политика безопасности содержимого) — самый мощный и самый капризный заголовок: его сложно настраивать и поддерживать. Если в политике есть default-src или script-src, браузер перестаёт выполнять встроенный JavaScript (<script> внутри страницы, onclick), а им часто пользуются CMS и плагины. Вернуть работу ключевым словом 'unsafe-inline' можно, но это лишает политику большей части смысла.
Поэтому действуем в два шага.
- Безопасное подмножество. Директивы
frame-ancestors,object-srcиbase-uriне мешают скриптам. Политикаframe-ancestors 'self'; object-src 'none'; base-uri 'self'разрешает встраивать страницы только вам самим, запрещает элементы<object>и<embed>и подмену адреса<base>. Сломаться могут встраивание на другие ваши домены и документы через<embed>. - Наблюдение за скриптами. Заголовок
Content-Security-Policy-Report-Onlyничего не блокирует, а лишь отправляет отчёты о нарушениях; без директивыreport-toон не даёт никакого эффекта, а приёмник отчётов (заголовокReporting-Endpoints) — отдельная тема. Строгая политика (скрипты поnonceили хешу) требует разбора отчётов. Для типового сайта на CMS с админкой разумно остановиться на шаге 1 (практика автора).
Где и как добавить
Соберём всё в один файл /etc/nginx/snippets/security-headers.conf. Каталог snippets подключается директивой include.
# /etc/nginx/snippets/security-headers.conf
add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy
"geolocation=(), camera=(), microphone=()" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy
"frame-ancestors 'self'; object-src 'none'; base-uri 'self'" always;
В блоке server с listen 443 ssl добавьте строку include snippets/security-headers.conf;, проверьте и примените: sudo nginx -t && sudo systemctl reload nginx. Две особенности add_header:
- без
alwaysзаголовок добавляется только к ответам с кодами 200, 201, 204, 206, 301, 302, 303, 304, 307 и 308, то есть не попадает на страницы ошибок 403, 404 и 500; - заголовки наследуются с предыдущего уровня, только если на текущем нет ни одной директивы
add_header. Стоит добавить её вlocation— и все заголовки уровняserverпропадают. Значит, в каждый такойlocationповторитеinclude. Директиваadd_header_inherit, меняющая это правило, есть только в новых версиях nginx (см. врезку «Состояние»).
Проверка снаружи; не забудьте и страницу ошибки:
curl -sI https://example.com/ \
| grep -i -E '^(strict|x-|referrer|permissions|content-sec)'
curl -sI https://example.com/net-takoj-stranitsy | grep -i x-content
Пример вывода первой команды (вымышленный, регистр и порядок строк зависят от версии):
strict-transport-security: max-age=300
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), camera=(), microphone=()
x-frame-options: SAMEORIGIN
content-security-policy: frame-ancestors 'self'; object-src 'none'; ...
Дубль строки: заголовок отправляет и приложение, оставьте одно место. Откат: закомментируйте include, выполните nginx -t и reload.
Права, пользователи и служебные файлы
Цель: взломанный сайт не должен мочь больше, чем ему положено. Три правила (практика автора на основе наименьших привилегий; для загруженных файлов то же советует OWASP): процесс, исполняющий код сайта, не может его изменять; он пишет только туда, где это нужно; у каждого сайта свой пользователь, чтобы взлом одного не открыл остальные.
| Что | Владелец | Права |
|---|---|---|
Код CMS, темы, плагины (/var/www/example) |
deploy — учётная запись, под которой вы выкладываете и обновляете сайт (или admin-lab) |
каталоги 755, файлы 644 (исполняемые остаются исполняемыми): пул читает, но не изменяет |
Загрузки (uploads/) |
site-example — пользователь пула сайта |
755 и 644: пул пишет, nginx читает |
| Кэш, временные файлы, сессии | site-example |
750 и 640 (выставьте вручную) |
| Секреты (пароль базы, ключи API) | root, группа site-example |
640, файл вне веб-корня |
Цена такой схемы: обновления ставите вы из командной строки (у большинства CMS она есть), а не кнопкой в админке; зато взломанная CMS не допишет вредоносный код в собственные файлы.
Заведём пользователей (deploy пропустите, если используете admin-lab) и выставим права. Сначала сохраните копию сайта (sudo cp -a /var/www/example /root/example.backup) и повторите упражнение на тестовом экземпляре: ошибка в chown -R и chmod -R по неверному пути ломает систему (см. Linux 04).
sudo adduser deploy
sudo adduser --system --group --no-create-home site-example
sudo chown -R deploy:deploy /var/www/example
sudo chmod -R u=rwX,go=rX /var/www/example
sudo chown -R site-example:site-example /var/www/example/uploads
# каталоги кэша и временных файлов вашей CMS: владелец site-example,
# каталоги chmod 750, файлы chmod 640
Затем создайте для сайта отдельный пул PHP-FPM. Директивы пула — практика автора; сверьте их с руководством PHP-FPM (php.net) для вашей версии. Файлы пулов в Ubuntu обычно лежат в /etc/php/<версия>/fpm/pool.d/.
; /etc/php/<версия>/fpm/pool.d/example.conf
[example]
user = site-example
group = site-example
listen = /run/php/php<версия>-fpm-example.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 5
Права сокета (listen.*) пускают к пулу только nginx. Директивы pm и pm.max_children обязательны (значения — практика автора, подберите по памяти): без них FPM не примет конфигурацию, а перезапуск с ошибкой в одном пуле может остановить PHP на всех сайтах. Сначала проверьте конфигурацию (перезапуск выполнится, только если проверка прошла):
sudo php-fpm<версия> -t && sudo systemctl restart php<версия>-fpm
В блоке location ~ \.php$ сайта укажите сокет пула: fastcgi_pass unix:/run/php/php<версия>-fpm-example.sock;. Проверьте nginx -t, примените reload и откройте сайт. Откат: удалить файл пула, вернуть прежний fastcgi_pass, перезапустить службу.
Что не должно отдаваться наружу
Каталоги .git, файлы .env, резервные копии и дампы в веб-корне отдают то, о чём вы не думали. Лучшая защита — не класть их туда. Страховка в nginx:
# скрытые файлы и каталоги (.git, .env) не отдаём
location ^~ /.well-known/ { }
location ~ /\. { return 404; }
# резервные копии и дампы, случайно попавшие в веб-корень
location ~* \.(bak|old|orig|swp|sql|log)$ { return 404; }
Первая строка нужна для проверки домена Let's Encrypt (подкаталог /.well-known/acme-challenge/): ^~ отменяет проверку регулярных выражений, и скрытое правило её не перехватит. Остальным каталогам (конфигурация, кэш, журналы вашей CMS) такие правила напишите по её документации. Секреты держите вне веб-корня и вне репозитория (Git для администратора): это советует и OWASP: не встраивать ключи и пароли в код и файлы конфигурации.
Проверка снаружи; запускайте её только против своего сайта. Ожидаемый ответ везде 404 (или 403), любой 200 — повод немедленно закрыть путь.
for p in /.git/HEAD /.env /backup.sql; do
printf '%s -> ' "$p"
curl -s -o /dev/null -m 10 -w '%{http_code}\n' "https://example.com$p"
done
Админ-панель, вход и загрузка файлов
Доступ к админке. Ставьте несколько преград сразу (A01 и A07). Первая — многофакторная проверка для всех администраторов CMS (Безопасность 02). Вторая — доступ только из офиса или через VPN (Безопасность 05). Для nginx это пара allow и deny: правила проверяются по порядку до первого совпадения.
location ^~ /admin/ { # путь админки зависит от вашей CMS
allow 192.168.10.0/24; # офисная сеть
allow 203.0.113.10; # адрес VPN-шлюза (пример)
deny all;
# здесь повторите директивы, которыми CMS обрабатывает запросы
# (например, fastcgi_pass для PHP)
}
Запрос обслуживает ровно один location, а ^~ отключает проверку регулярных выражений: общий location ~ \.php$ сюда не попадёт, поэтому внутри блока нужны свои директивы обработки. Если админка стоит за прокси или CDN (сетью доставки содержимого), nginx видит адрес прокси, и правило пустит всех или никого. Проверяйте результатом: с чужого адреса https://example.com/admin/ должен отвечать 403. Держите вторую открытую сессию SSH; откат — удалить allow и deny, nginx -t, reload. Нестандартный путь админки защитой не считается: меняйте его только сверх перечисленного.
Формы и частота запросов. От подделки межсайтовых запросов (CSRF: чужая страница заставляет браузер вошедшего пользователя отправить запрос на ваш сайт) защищает сама CMS токенами в формах; не отключайте эту защиту (практика автора). Быстрый перебор паролей на странице входа замедлит модуль ngx_http_limit_req_module: он ограничивает скорость запросов с одного адреса методом «дырявого ведра». Зона объявляется в контексте http (например, в /etc/nginx/conf.d/limits.conf), а ограничение — в location страницы входа:
# /etc/nginx/conf.d/limits.conf
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
location = /admin/login { # путь входа зависит от вашей CMS
limit_req zone=login burst=5 nodelay;
# далее директивы обработки, как у остальных страниц CMS
}
Числа 10r/m и burst=5 — пример и практика автора, подберите их по журналу входов. Это в среднем 10 запросов в минуту с адреса и «пачка» до 5 запросов сверх нормы; остальные получают ошибку 503 (код по умолчанию, меняется директивой limit_req_status), а отказ записывается в журнал ошибок. Если весь офис выходит в интернет с одного адреса, он делит один лимит: повышайте burst или исключите офисный адрес. Лимит по адресу не остановит перебор с тысячи адресов, поэтому он дополняет MFA и блокировку, но не заменяет их.
Загрузка файлов. Если сайт принимает файлы, это самая опасная функция. Правила OWASP: разрешайте только нужные расширения (белый список), проверяйте тип файла, а не заголовок Content-Type (его можно подделать), заменяйте имя файла сгенерированным, храните файлы на другом сервере или хотя бы вне веб-корня, ограничивайте размер, разрешайте загрузку только авторизованным и пропускайте файлы через антивирус, если он есть. В nginx размер ограничивает директива client_max_body_size (по умолчанию 1 МБ). Если каталог загрузок всё же лежит в веб-корне, исключите запуск кода из него:
# каталог загрузок: файлы только отдаём, PHP не запускаем
location ^~ /uploads/ { }
Из-за ^~ общий location ~ \.php$ сюда не попадёт, и загруженный файл с расширением .php будет отдан как файл, а не выполнен. А nosniff из заголовков не даст браузеру выполнить такой файл как HTML, если сервер объявил другой тип.
Обновления, WAF, копии и мониторинг
Обновления. Категория A03 выросла из «уязвимых и устаревших компонентов» (A06:2021): устаревшая CMS, тема или плагин — привычный путь на сайт. Порядок: копия, обновление на тестовой копии, затем на боевом сайте, проверка. Удаляйте неиспользуемые плагины и темы: OWASP называет «минимальную платформу без лишних компонентов, документации и примеров» мерой против A02. Компоненты берите только у официальных поставщиков. Сроки и приоритеты — в Безопасность 03.
WAF. Он блокирует попытки использовать известную уязвимость, не меняя уязвимый код; OWASP называет это «виртуальным патчем» и в качестве веб-серверного модуля упоминает ModSecurity. Но уязвимость он не устраняет: обновление всё равно нужно.
Копии. Копируйте файлы сайта, базу, конфигурацию nginx и PHP-FPM, секреты (в зашифрованном виде) и /etc/letsencrypt. Схема — в статьях про правило 3-2-1 и Linux 15; главное — регулярно восстанавливать сайт на тестовой машине.
Мониторинг. Следите за доступностью, сроком сертификата и закрытыми путями (Zabbix с нуля). В веб-сценарии Zabbix у шага есть параметр «Required status codes»: если код ответа не входит в список, шаг считается неудачным. Укажите для /.git/HEAD код 404, и вы узнаете, если путь вдруг откроется. Если сайт уже взломан, действуйте по плану из Безопасность 10: отключите уязвимое, сохраните журналы, восстановите из чистой копии и смените пароли и ключи.
Tip
Чек-лист.
- HTTP перенаправляет на HTTPS,
ssl_protocols— толькоTLSv1.2 TLSv1.3, автопродление проверено. - HSTS поднят ступенями;
includeSubDomainsиpreloadвключены осознанно или не включены. - Заголовки приходят с
always, в том числе на страницах ошибок (провереноcurl -I). - У сайта свой пользователь пула; код для PHP только для чтения; пишется лишь нужное.
.git,.env, дампы и копии не лежат в веб-корне; снаружи они отвечают 404 или 403.- Админка закрыта MFA и доступом по адресам или VPN; страница входа — с ограничением частоты.
- Загрузки: белый список расширений, лимит размера, код в каталоге загрузок не запускается.
- CMS, плагины и PHP обновляются; восстановление проверено; сертификат и закрытые пути под мониторингом.
Состояние на ноябрь 2026 года (проверьте актуальность). OWASP Top 10:2025 — текущая редакция на сайте проекта (восьмое издание). Руководство Mozilla по TLS имеет версию 6.0 (апрель 2026 года); генератор передан сообществу проекта TLSRef, и
ssl-config.mozilla.orgперенаправляет туда, поэтому списки шифров сверяйте по свежему выводу. Директиваadd_header_inheritесть с nginx 1.29.3, а в пакетах Ubuntu 24.04 и 26.04 стоят nginx 1.24 и 1.28. Профиль Intermediate в версии 6.0 рассчитан на клиентов вплоть до Chrome 49, Firefox 31.3, Safari 9 и Android 4.4.2.
Типичные ошибки и недоразумения
- Заголовки без
always. Их нет на страницах 403, 404 и 500, и проверка «главной» этого не покажет. - Заголовки пропали после правки
location. Одинadd_headerв блоке отменяет унаследованные: повторитеinclude. - HSTS сразу на год с
includeSubDomains. Забытый поддомен без HTTPS становится недоступным, а отозвать правило быстро нельзя. - Строгая CSP «с первого раза». Сайт перестаёт загружать скрипты. Сначала режим наблюдения;
'unsafe-inline'вернёт работу, но обесценит политику. allowиdenyв префиксномlocation, а PHP выполняет регулярный. Правило не работает. Проверяйте результат с чужого адреса, а не только конфигурацию.- Права «всем всё» (
chmod -R 777). Антипример: любой взломанный скрипт перепишет сайт.
Проверьте себя
- Почему недостаточно написать
add_header X-Content-Type-Options "nosniff";безalways? Безalwaysего нет в ответах с кодами вне короткого списка (200, 204, 301 и ещё нескольких): на страницах ошибок 403, 404 и 500 он не появится. - Как откатить HSTS, если он включён ошибочно?
Отправлять
max-age=0по HTTPS и держать HTTPS рабочим, пока не истечёт запомненный браузерами срок. - Когда безопасно включать
includeSubDomains? Когда все поддомены, включая забытые и внутренние, работают по HTTPS с действующим сертификатом. - Как убедиться, что
/.git/недоступен снаружи? Запросить/.git/HEADу своего сайта командойcurl -s -o /dev/null -w '%{http_code}': ожидается 404 или 403, а не 200. - Почему
deny allвlocation /admin/может не защитить/admin/index.php? Запрос может обработать регулярныйlocation ~ \.php$, если у префикса нет^~: правила префикса к нему не применяются.
Что дальше
- Безопасность для начинающих 10 — что делать, если сайт или сервер уже взломан, и как восстанавливаться.
- Безопасность для начинающих 07 — что логировать и о чём оповещать.
- Zabbix с нуля — мониторинг доступности, сертификата и закрытых путей.
Источники
- OWASP Top 10:2025, введение и страницы категорий A01–A10 — категории, изменения, описания и меры
- OWASP HTTP Headers Cheat Sheet — значения заголовков,
Server,X-Powered-By, заменаX-Frame-Options - OWASP File Upload Cheat Sheet — правила приёма файлов
- OWASP Virtual Patching Cheat Sheet — что делает WAF и чего не делает
- MDN: Strict-Transport-Security — синтаксис,
includeSubDomains,preload,max-age=0 - MDN: Content-Security-Policy — директивы,
'self','unsafe-inline', встроенный JS - MDN: Content-Security-Policy-Report-Only — режим наблюдения и
report-to - MDN: X-Content-Type-Options — действие
nosniff - MDN: X-Frame-Options — значения
DENYиSAMEORIGIN - nginx: ngx_http_headers_module —
add_header,always, наследование,add_header_inherit - nginx: ngx_http_limit_req_module —
limit_req_zone,limit_req,burst,nodelay, код 503 - nginx: ngx_http_access_module —
allowиdeny, порядок проверки - nginx: ngx_http_ssl_module —
ssl_protocols,ssl_ciphers, кэш и срок сессий, умолчания - Mozilla SSL Configuration: руководство 6.0 (JSON) — профили Modern и Intermediate, протоколы, самые старые клиенты
- Zabbix: веб-мониторинг — шаги веб-сценария и параметр «Required status codes»