Безопасность сайта: как защитить сайт на каждом уровне (2026)

Ключевой вывод

Безопасность сайта строится уровнями: закрепите за собой домен и DNS (registrar lock, DNSSEC, CAA), включите современный TLS с HSTS, отправляйте строгий набор HTTP-заголовков безопасности, хешируйте пароли с Argon2id и прикройте вход MFA и rate limiting, проверяйте входные данные и права доступа на сервере, фиксируйте версии зависимостей и проводите их аудит, закройте все порты, которые вы не обслуживаете, и ведите журналы, по которым можно заметить взлом. Для каждой практики ниже приведены точная конфигурация и бесплатный способ убедиться, что она действительно работает: меры защиты, которую вы не проверили, у вас фактически нет.

Проверьте сейчас

Безопасность сайта: шесть уровней защиты — домен и DNS, TLS, HTTP-заголовки, приложение, зависимости и сервер
Безопасность сайта: шесть уровней защиты — домен и DNS, TLS, HTTP-заголовки, приложение, зависимости и сервер
Начать чтение
  1. Что на самом деле означает безопасность сайта
  2. Стек веб-безопасности: шесть уровней защиты
  3. Уровень 1: защитите домен и DNS
  4. Уровень 2: HTTPS и TLS как положено
  5. Уровень 3: HTTP-заголовки безопасности
  6. Уровень 4: аутентификация, устойчивая к credential stuffing
  7. Сессии, cookie и CSRF
  8. Инъекции и XSS: проверяйте ввод, кодируйте вывод
  9. Broken Access Control: риск номер один
  10. Уровень 5: зависимости и цепочка поставок ПО
  11. Уровень 6: защита сервера (порты, патчи, секреты, резервные копии)
  12. Аутентификация почты: SPF, DKIM и DMARC
  13. Логируйте, мониторьте и готовьтесь к взлому
  14. Проверяйте: сканеры, пентесты и непрерывный контроль
  15. Чек-лист безопасности сайта
  16. Как это соотносится с OWASP Top 10:2025
  17. Частые вопросы о безопасности сайта

Что на самом деле означает безопасность сайта

Безопасность сайта — это набор мер защиты, которые не дают взломать сайт или веб-приложение, подменить его содержимое, использовать его для атак на собственных посетителей или незаметно выкачать из него данные. Это не один продукт и не одна настройка. Это цепочка решений, которая начинается у регистратора домена и проходит через DNS, TLS, HTTP-заголовки, которые отправляет ваш сервер, код, обрабатывающий вход в систему и пользовательский ввод, сторонние пакеты, на которых построено приложение, сам сервер и, наконец, журналы, из которых вы узнаёте, что что-то пошло не так.

Причина мыслить уровнями в том, что именно так мыслят атакующие. В отчёте Verizon 2026 Data Breach Investigations Report, охватившем более 22 000 утечек с ноября 2024 по октябрь 2025 года, сказано: 31% утечек начались с эксплуатации уязвимости в ПО, тогда как годом ранее было 20%, и впервые это больше, чем украденные учётные данные (13%); доля фишинга осталась на уровне 16%. Программы-вымогатели участвовали в 48% утечек против 44%. Достаточно одного непропатченного обновления, утёкшего API-ключа или админки без rate limiting. Ничего экзотического в этом руководстве нет: взламывают, как правило, те сайты, где на одном уровне пропустили базовые меры, пока шлифовали другой.

Руководство построено снаружи внутрь. Для каждой меры указано зачем она нужна, что именно настроить и как это проверить командой или бесплатным инструментом, например проверкой HTTP-заголовков или проверкой SSL-сертификата: настройка, которая, как вам кажется, включена, но ни разу не проверена, ничего не защищает.

Проверьте заголовки безопасности сайта

Стек веб-безопасности: шесть уровней защиты

Любая атака на сайт приходится на один из шести уровней. Таблица ниже — карта для всего остального руководства: что находится на каждом уровне, как его обычно атакуют и какая бесплатная проверка подтвердит, что ваша защита действительно работает.

УровеньЧто делают атакующиеОсновные практикиКак проверить
1. Домен и DNSУгоняют домен, меняют NS-серверы, захватывают «висячие» поддомены, получают поддельные сертификатыRegistrar lock, MFA, DNSSEC, CAA, аудит устаревших записейWHOIS-поиск, Проверка DNS домена, Поиск поддоменов
2. Транспорт (TLS)Понижают соединение до HTTP, снимают шифрование, эксплуатируют устаревшие шифры и просроченные сертификатыTLS 1.2+, HSTS (preload по желанию), автоматическое продлениеПроверка SSL-сертификата
3. HTTP-заголовкиВнедряют скрипты (XSS), встраивают сайт во фрейм для кликджекинга, пользуются угадыванием типа содержимого, перехватывают утечки через RefererCSP, frame-ancestors, nosniff, Referrer-Policy, Permissions-PolicyПроверка HTTP-заголовков
4. ПриложениеCredential stuffing, инъекции, нарушение контроля доступа, CSRF, небезопасная загрузка файловArgon2id + MFA + rate limiting, параметризованные запросы, авторизация на сервере, cookie с SameSiteКод-ревью, ZAP, Тест надёжности пароля
5. ЗависимостиОтравленные пакеты, известные CVE в библиотеках, скомпрометированные CI-пайплайныLockfile, аудит, фиксированные версии, SBOM, токены с минимальными правамиnpm audit, Dependabot, Trivy
6. Сервер и эксплуатацияОткрытые порты, учётные данные по умолчанию, отсутствующие патчи, нет резервных копий, нет журналовФайрвол, SSH только по ключам, патчи, резервное копирование 3-2-1, централизованное логированиеПроверка портов, Проверка IP в чёрных списках

Двигайтесь сверху вниз. Уровни 1–3 — это конфигурация, которую можно закончить за полдня, и она защищает сразу все страницы. Уровни 4–6 — постоянные привычки, которые должны стать частью код-ревью и процесса деплоя.

Уровень 1: защитите домен и DNS

Всё остальное в этом руководстве предполагает, что домен по-прежнему под вашим контролем. Если атакующий может войти в ваш аккаунт у регистратора или сменить NS-серверы, он направит ваш трафик куда угодно, получит действительный сертификат на ваше имя и будет читать всю адресованную вам почту — а ни одна строка кода вашего приложения даже не выполнится. Поэтому защита домена и DNS — первая из лучших практик веб-безопасности, и почти целиком это вопрос конфигурации.

Начните с WHOIS-поиска по собственному домену и убедитесь в трёх вещах: среди статусов есть clientTransferProhibited, до окончания регистрации больше года, а регистратор — тот, у которого у вас действительно есть аккаунт. Удивительно часто домен компании числится в аккаунте бывшего подрядчика. В нашем руководстве по WHOIS-поиску разобраны все коды статусов, которые вы увидите.

Registrar lock, MFA и напоминания об окончании срока

Включите registrar lock (его также называют блокировкой трансфера), чтобы домен нельзя было перенести к другому регистратору, пока вы явно не снимете блокировку, и включите многофакторную аутентификацию в самом аккаунте регистратора. Аккаунты у регистраторов — излюбленная цель фишинга именно потому, что один вход открывает доступ ко всему, что от него зависит. Если регистратор позволяет, используйте аппаратный ключ или приложение-аутентификатор, а не SMS.

Включите автопродление домена с платёжным средством, срок действия которого не истечёт, и всё равно поставьте напоминание в календаре за 60 дней до окончания регистрации. Просроченный домен пропадает не в день окончания, но когда льготный период и период выкупа заканчиваются (для .com это примерно до 80 дней), домен удаляется, а сервисы drop-catch регистрируют его в течение нескольких секунд после освобождения. Покупатель получает вашу почту, ваш трафик и все OAuth-входы, которые доверяют вашему домену.

  • Registry lock (ручная блокировка на уровне реестра, снимаемая только через отдельный канал) стоит дополнительных денег для домена, от которого зависит бизнес: он не даёт сменить NS-серверы даже при взломанном аккаунте у регистратора.
  • Разделите роли. Кто платит за домен, какой аккаунт им владеет и кто предоставляет DNS — всё это должно быть задокументировано и не привязано к почтовому ящику одного сотрудника.
  • Включите защиту данных в WHOIS, чтобы ваши контакты не стали картой для фишинга, и укажите в аккаунте ролевой адрес, который кто-то регулярно читает, например domains@.

Подпишите зону с помощью DNSSEC

DNSSEC подписывает DNS-зону, чтобы резолвер мог обнаружить поддельные ответы. Без него атакующий, который может отравить кэш резолвера, отправит ваших посетителей на поддельный сервер, а в браузере по-прежнему будет виден ваш домен. Большинство управляемых DNS-провайдеров (Cloudflare, Route 53, Google Cloud DNS и многие регистраторы) включают его одним переключателем; единственный ручной шаг — опубликовать DS-запись у регистратора. Проверьте через dig или с помощью проверки DNS домена, которая проверяет DS-запись и подписи:

bash
dig +dnssec +short yourdomain.com A
# A signed zone returns the record AND an RRSIG line:
# 203.0.113.10
# A 13 2 3600 20270115000000 20261225000000 34505 yourdomain.com. Kx3f...==

dig +short yourdomain.com DS
# A DS record at the parent zone proves the chain of trust is complete
# 2371 13 2 6A1B...C9

Ограничьте выпуск сертификатов записью CAA

Запись CAA (RFC 8659) сообщает центрам сертификации, кому из них разрешено выпускать сертификаты для вашего домена. Каждый публичный центр сертификации обязан проверять её перед выпуском, поэтому запись CAA закрывает дверь атакующему, который сумел обмануть проверку домена в каком-то другом центре. Три записи покрывают типичные случаи: кто может выпускать сертификаты, кто может выпускать wildcard-сертификаты и куда сообщать об отклонённом запросе:

dns
yourdomain.com.  CAA 0 issue "letsencrypt.org"
yourdomain.com.  CAA 0 issuewild ";"
yourdomain.com.  CAA 0 iodef "mailto:security@yourdomain.com"

Аудит устаревших записей: subdomain takeover

Захват поддомена происходит, когда DNS-запись всё ещё указывает на сервис, которым вы перестали пользоваться: CNAME на удалённый сайт GitHub Pages, приложение Azure, бакет S3 или имя хоста стороннего SaaS. Любой может зарегистрировать брошенную цель и затем отдавать контент на old-app.yourdomain.com от вашего имени, причём с действительным сертификатом. Это регулярная находка в программах bug bounty, потому что никто не помнит о существовании записи.

Прогоните домен через Поиск поддоменов: он ищет в двух сервисах журналов Certificate Transparency, Cert Spotter (действующие сертификаты) и crt.sh (все сертификаты с 2013 года), перебирает 1000 распространённых имён, разрешает каждое имя вживую, помечает «висячие» CNAME и показывает, какой хост или CDN обслуживает каждое имя. Удалите каждую запись, цель которой возвращает NXDOMAIN или страницу провайдера «такого приложения нет»; одно конкретное имя подтвердит DNS-поиск. Повторяйте раз в квартал и включите удаление DNS-записи в процедуру вывода любого сервиса из эксплуатации.

Certificate Transparency — это ещё и система раннего предупреждения: Cert Spotter и CT-мониторинг Cloudflare могут присылать письмо каждый раз, когда любой центр сертификации выпускает сертификат для вашего домена. Неожиданный сертификат часто становится первым видимым признаком компрометации DNS или аккаунта у регистратора.

Уровень 2: HTTPS и TLS как положено

HTTPS давно не «лучшая практика», а базовый минимум: браузеры помечают обычные HTTP-страницы как «Не защищено». Кроме того, Google объявил, что начиная с Chrome 154 (октябрь 2026 года) браузер будет спрашивать подтверждение перед открытием любого публичного сайта по обычному HTTP (Google). То, что по-прежнему отличает защищённый сайт от просто зашифрованного, — это принимаемые версии TLS, возможность вообще обратиться к вам по HTTP и достаточная автоматизация продления, чтобы пережить сокращение срока действия сертификатов, начавшееся в марте 2026 года.

Минимум TLS 1.2, предпочтительно TLS 1.3

TLS 1.0 и 1.1 официально признаны устаревшими в RFC 8996 в 2021 году, и ни один современный браузер их не согласует. Отключите их на сервере, отдавайте предпочтение TLS 1.3 (в нём нет ни одного известного слабого шифра, а рукопожатие занимает один круговой обмен) и оставляйте TLS 1.2 только с AEAD-наборами шифров, например ECDHE-ECDSA-AES128-GCM-SHA256 или ECDHE-RSA-CHACHA20-POLY1305.

Когда проверяете снаружи, важны две строки: согласованный протокол и Verify return code: 0, означающий, что вся цепочка сертификатов прошла проверку. Отсутствующий промежуточный сертификат — частая причина жалоб вида «в Chrome работает, а в curl и на Android нет»; как это исправить, показывает наше руководство по цепочке SSL-сертификатов, а проверка SSL-сертификата напрямую сообщает о неполной цепочке. Вот проверка dnsrobot.net на 27 октября 2026 года, а затем тест, что сервер отклоняет TLS 1.1:

bash
echo | openssl s_client -connect dnsrobot.net:443 -servername dnsrobot.net 2>/dev/null \
  | grep -E 'Protocol|Cipher|Verify return'

# New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
# Protocol: TLSv1.3
# Verify return code: 0 (ok)

# Is TLS 1.1 refused? SECLEVEL=0 lets OpenSSL 3 and later offer it at all
openssl s_client -connect example.com:443 -servername example.com -tls1_1 \
  -cipher 'DEFAULT:@SECLEVEL=0' </dev/null 2>&1 | grep -E 'alert|Cipher is'

# Refused, as it should be:  ...tlsv1 alert protocol version...
#                            New, (NONE), Cipher is (NONE)
# Still accepted (fix it):   New, TLSv1.0, Cipher is ECDHE-ECDSA-AES128-SHA
#   (TLSv1.0 is the cipher's version; the handshake was TLS 1.1)

Списки шифров — место, где скопированные конфигурации устаревают. Вместо того чтобы писать их вручную, сгенерируйте актуальный рекомендуемый блок для nginx, Apache, Caddy или HAProxy в Mozilla SSL Configuration Generator и выбирайте профиль «Intermediate», если только вам не нужна поддержка очень старых клиентов.

HSTS: запретите браузеру возвращаться к HTTP

Одного редиректа с HTTP на HTTPS недостаточно. Самый первый запрос посетителя к http://yourdomain.com всё равно уходит открытым текстом, и атакующий в той же сети может ответить на него раньше, чем сработает ваш редирект. HTTP Strict Transport Security (RFC 6797) решает эту проблему: увидев заголовок один раз, браузер переписывает каждый последующий URL http:// для вашего домена на https:// ещё до отправки запроса.

Задайте max-age не меньше одного года (31536000 секунд) и добавьте includeSubDomains, когда все поддомены будут работать по HTTPS. Добавление домена в preload-список на hstspreload.org, который зашивает домен в Chrome, Firefox, Safari и Edge, необязательно: сами авторы списка теперь пишут, что HSTS рекомендуется, а preload нет, потому что Chrome и Safari и так переводят переходы на HTTPS, а выйти из списка получается лишь за месяцы. Проверьте заголовок в проверке HTTP-заголовков, которая оценивает HSTS тестами Mozilla HTTP Observatory.

http
Strict-Transport-Security: max-age=31536000; includeSubDomains

Сертификаты на 47 дней: автоматизируйте продление уже сейчас

Голосование CA/Browser Forum SC-081 поэтапно сокращает максимальный срок действия всех публичных TLS-сертификатов. Первое сокращение вступило в силу 15 марта 2026 года, так что любой сертификат, который вы покупаете или продлеваете сегодня, уже ограничен 200 днями, а к 2029 году сертификат придётся менять примерно каждые шесть недель:

Дата вступления в силуМаксимальный срок действия сертификатаПовторное использование проверки домена
До 15 марта 2026 года398 дней398 дней
15 марта 2026 года200 дней200 дней
15 марта 2027 года100 дней100 дней
15 марта 2029 года47 дней10 дней

Ручное продление такой график не переживёт. Лучшая практика — полностью автоматический выпуск через ACME: Let's Encrypt или Google Trust Services через certbot, acme.sh, Caddy или встроенные автоматические сертификаты Cloudflare, Vercel, Netlify и большинства панелей хостинга. Проверьте продление заранее, до того как оно понадобится (certbot renew --dry-run для certbot); ищите прежде всего запись CAA или правило файрвола, добавленные после прошлого продления и теперь блокирующие проверку домена. Что бы вы ни использовали, следите за сроком действия и снаружи: проверка SSL-сертификата показывает, сколько дней осталось, а просроченный сертификат превращает каждый визит в полноэкранное предупреждение браузера.

Уровень 3: HTTP-заголовки безопасности

Заголовки безопасности — это инструкции, которые сервер даёт браузеру о том, что можно и чего нельзя делать с вашими страницами: какие скрипты могут выполняться, можно ли встраивать страницу во фрейм, может ли браузер угадывать тип содержимого и что сообщать другим сайтам о том, откуда пришёл посетитель. Они ничего не стоят, применяются сразу ко всем страницам и блокируют целые классы атак. Кроме того, именно на этом уровне большинство сайтов хотя бы частично ошибается, поэтому проверка HTTP-заголовков оценивает их тестами Mozilla HTTP Observatory. Вот что отправлял github.com 27 октября 2026 года, урезано до заголовков безопасности (в его script-src указаны один хост с ресурсами и хеш скрипта, без 'unsafe-inline'):

bash
curl -sI https://github.com/ | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer-policy'

strict-transport-security: max-age=31536000; includeSubdomains; preload
x-frame-options: deny
x-content-type-options: nosniff
referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin
content-security-policy: default-src 'none'; base-uri 'self'; ... frame-ancestors 'none'; ... script-src github.githubassets.com 'sha256-6AKuI+cDd1MoUL1u4eqrA9PXmnPAkOB4h7421hPUUm0='; ...

Семь заголовков, которые должен отправлять каждый сайт

Вот значения, к которым стоит стремиться на обычном сайте. В оценке Mozilla HTTP Observatory, которую использует наша проверка HTTP-заголовков, отсутствие CSP стоит 25 баллов, отсутствие HSTS или защиты от встраивания во фрейм по 20, а отсутствие nosniff 5; Referrer-Policy и COOP добавляют бонусные баллы, если счёт всё ещё 90 или выше. Permissions-Policy в оценке не участвует (его читают только браузеры на Chromium), но задать его всё равно стоит.

ЗаголовокРекомендуемое значениеОт чего защищает
Strict-Transport-Securitymax-age=31536000; includeSubDomainsПонижение протокола, кража cookie по HTTP
Content-Security-Policyscript-src на основе nonce с 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'Межсайтовый скриптинг (XSS), внедрение данных, кликджекинг
X-Content-Type-OptionsnosniffMIME-сниффинг, превращающий загруженный файл в исполняемый скрипт
Referrer-Policystrict-origin-when-cross-originУтечка полных URL (токенов, поисковых запросов) третьим сторонам
Permissions-Policycamera=(), microphone=(), geolocation=(), payment=()Скрытое использование мощных API браузера сторонними скриптами
X-Frame-OptionsDENY (устаревший запасной вариант для frame-ancestors)Кликджекинг в браузерах без поддержки CSP Level 2
Cross-Origin-Opener-Policysame-originМежоконные атаки через window.opener

Content Security Policy, которая не ломает сайт

Content Security Policy — самая эффективная защита от XSS: она не даёт внедрённым скриптам выполниться, даже если уязвимость для инъекции существует. Подвох в том, что строгая политика ломает любой встроенный скрипт или сторонний тег, о котором вы забыли. Современный подход, который рекомендует Google и который описан на MDN, — политика на основе nonce: сервер генерирует случайный nonce для каждого ответа, добавляет его в каждый тег script, который выводит намеренно, а всё остальное браузер выполнять отказывается.

'strict-dynamic' делает такую политику практичной: скрипт с nonce может подгружать другие скрипты (аналитику, менеджеры тегов, виджеты), и не нужно вносить каждый из них в белый список, а токены https: и 'unsafe-inline' современные браузеры игнорируют — они служат лишь запасным вариантом для старых. Начните с Content-Security-Policy-Report-Only, неделю понаблюдайте за отчётами о нарушениях, затем переключитесь в режим блокировки. У Next.js, Rails и Django есть встроенная поддержка nonce; статические сайты могут вместо этого использовать script-src на основе хешей.

http
Content-Security-Policy:
  script-src 'nonce-r4nd0m1z3dV4lu3' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';
  frame-ancestors 'none';
  form-action 'self';
  report-to csp-endpoint

<!-- Only scripts carrying the nonce run; strict-dynamic lets them load their own dependencies -->
<script nonce="r4nd0m1z3dV4lu3" src="/app.js"></script>

Политика на основе белого списка, например script-src 'self' https://cdn.example.com, лучше, чем ничего, но любой JSONP-эндпоинт или старая библиотека на разрешённом хосте позволит её обойти. Если вы пока не можете использовать nonce, задайте хотя бы object-src 'none' и base-uri 'none' — это сразу закрывает два распространённых способа обхода.

Кликджекинг: frame-ancestors и X-Frame-Options

При кликджекинге ваш сайт невидимо загружается внутри страницы атакующего, и посетителя вынуждают нажать кнопку, которую он не видит. frame-ancestors 'none' в CSP (или 'self', если вы встраиваете собственные страницы) предотвращает это во всех современных браузерах, а X-Frame-Options: DENY защищает старые. Если вы сознательно предлагаете встраиваемый виджет, сделайте исключение только для этого пути и только для тех источников, которым оно нужно; в нашем руководстве по X-Frame-Options разобраны точные серверные правила и ошибки «refused to connect», с которыми вы столкнётесь при тестировании.

nosniff, Referrer-Policy, Permissions-Policy и COOP

X-Content-Type-Options: nosniff не даёт браузеру переопределять Content-Type на свой лад — а именно так загруженное пользователем «изображение» с HTML внутри превращается в исполняемую страницу. Referrer-Policy: strict-origin-when-cross-origin (значение по умолчанию в современных браузерах, но задайте его явно) передаёт другим сайтам только ваш источник (origin), поэтому токены сброса пароля и поисковые запросы в URL остаются приватными. Permissions-Policy отключает функции браузера, которые вы не используете, чтобы скомпрометированный сторонний скрипт не мог включить камеру или прочитать геолокацию. Cross-Origin-Opener-Policy: same-origin разрывает связь window.opener со страницами, которые вы открываете, и блокирует целый класс межоконных атак.

Удалять заголовки тоже важно: Server и X-Powered-By сообщают сканерам уязвимостей точные версии ПО, а X-XSS-Protection устарел и в старых браузерах может порождать ошибки, поэтому задайте ему 0 или уберите. Определение CMS показывает, что любой может узнать о вашем стеке и его версиях прямо со страницы.

Задавайте заголовки один раз на периметре (CDN, обратный прокси или middleware фреймворка), а не для каждой страницы отдельно. В Next.js это файл proxy или middleware; в nginx — блок add_header в контексте server; в Apache — Header always set. Затем после каждого деплоя заново запускайте проверку HTTP-заголовков: новая версия фреймворка или настройка CDN может незаметно убрать один из них.

Уровень 4: аутентификация, устойчивая к credential stuffing

Атакующие редко подбирают пароли по одному. Они прогоняют миллиарды пар «email — пароль», утёкших с других сайтов (credential stuffing), а остальное добывают фишингом. Поэтому лучшая практика аутентификации состоит из трёх частей: хранить пароли так, чтобы утечка базы данных не стала утечкой паролей, сделать украденный пароль бесполезным самим по себе и сделать перебор слишком медленным, чтобы он имел смысл. В OWASP Top 10:2025 категория Authentication Failures (ошибки аутентификации) занимает позицию A07.

Хешируйте пароли с Argon2id или bcrypt

Никогда не храните пароли в открытом виде и никогда не храните их быстрые хеши. MD5, SHA-1 и даже SHA-256 спроектированы быстрыми, поэтому утёкшую таблицу таких хешей можно перебирать со скоростью миллиардов попыток в секунду на одной видеокарте. Используйте медленную функцию хеширования паролей с высокими требованиями к памяти и уникальную соль для каждого пароля. OWASP Password Storage Cheat Sheet рекомендует в таком порядке:

  • Argon2id минимум с 19 МиБ памяти, 2 итерациями и степенью параллелизма 1 (или 46 МиБ с 1 итерацией).
  • scrypt с N = 2^17, r = 8, p = 1, если Argon2 недоступен.
  • bcrypt с коэффициентом сложности (work factor) 10 или выше (учитывайте ограничение на входные данные в 72 байта: предварительное хеширование более длинных паролей требует аккуратности).
  • PBKDF2-HMAC-SHA256 с 600 000 итераций — только если этого требует соответствие FIPS.
javascript
// Node.js with the argon2 package
import argon2 from "argon2";

const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 19456, // KiB = 19 MiB
  timeCost: 2,
  parallelism: 1,
});

// Later, on login:
const ok = await argon2.verify(hash, submittedPassword);

Подберите стоимость так, чтобы один хеш считался примерно 100–250 мс на сервере входа. Реальный пользователь этого не заметит, но каждое ядро процессора будет перебирать лишь горстку вариантов в секунду, а даже мощная видеокарта — тысячи, а не миллиарды. Пересчитывайте хеш при успешном входе всякий раз, когда повышаете параметры, чтобы старые хеши обновлялись сами.

Правила для паролей, которые действительно помогают (NIST SP 800-63B)

Правила состава, которые большинство сайтов всё ещё навязывает (одна заглавная буква, один спецсимвол, смена каждые 90 дней), порождают предсказуемые пароли вроде Summer2026! и подталкивают людей использовать их повторно. Актуальные рекомендации NIST SP 800-63B заменяют их правилами, которые измеряют то, что действительно важно:

  • Минимум 8 символов, если также требуется MFA, и не менее 15 символов для пароля, который используется без второго фактора; разрешайте длину не меньше 64 символов.
  • Никаких правил состава и никакой принудительной периодической смены; требуйте смену пароля только при признаках компрометации.
  • Проверяйте каждый новый пароль по базе утечек (range API сервиса Have I Been Pwned позволяет делать это, не передавая сам пароль) и отклоняйте пароли, которые уже утекли.
  • Разрешайте вставку из буфера обмена и менеджеры паролей, принимайте пробелы и Unicode и давайте подсказки, которые помогают выбрать надёжный пароль, особенно сразу после того, как вы отклонили предыдущий.

Наш Тест надёжности пароля показывает, чем эти меры отличаются от старых правил: он оценивает, за какое время пароль взламывается при 8 именованных атаках, показывает шаблоны, которые его ослабляют, и проверяет пароль по базам утечек, не отправляя его, — это гораздо полезнее красного сообщения «добавьте спецсимвол».

MFA и passkeys

Многофакторная аутентификация превращает украденный пароль в тупик. Предлагайте её всем и делайте обязательной для администраторов, финансовых сотрудников и службы поддержки. Ранжируйте варианты по устойчивости к фишингу: passkeys и ключи безопасности FIDO2 невозможно выманить фишингом, потому что учётные данные привязаны к вашему настоящему источнику (origin); приложения-аутентификаторы (TOTP) — хороший вариант; SMS-коды лучше, чем ничего, но их можно перехватить через подмену SIM-карты (SIM swapping). Passkeys поддерживаются во всех современных браузерах и операционных системах и полностью избавляют от пароля, а значит, и от credential stuffing как класса атак.

Защищайте путь восстановления так же тщательно, как вход: храните коды восстановления в виде хешей, для сброса по email используйте одноразовые токены, которые истекают в течение 15 минут, и откажитесь от контрольных вопросов.

Rate limiting, блокировка и защита от ботов

Ограничивайте частоту запросов к эндпоинтам входа, регистрации, сброса пароля и MFA по IP-адресу и по учётной записи, а после достижения лимита возвращайте 429 Too Many Requests с заголовком Retry-After (в нашем руководстве по ошибке HTTP 429 описано, как должны реагировать клиенты). Дополните это нарастающей задержкой или временной блокировкой после повторных неудачных попыток входа в одну учётную запись, а для эндпоинтов, на которые идут распределённые атаки, — CAPTCHA или проверкой proof-of-work. Записывайте в журнал каждую неудачную попытку входа с IP-адресом источника, чтобы картина атаки credential stuffing была видна через минуты, а не через месяцы.

nginx
# nginx: 5 login attempts at once, then 1 every 12 seconds, per client IP
# (limit_req sends no Retry-After header; add one on your 429 page if clients need it)
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

location = /api/login {
    limit_req zone=login burst=4 nodelay;
    limit_req_status 429;
    proxy_pass http://app;
}

Показывайте одинаковое сообщение об ошибке для «неизвестного пользователя» и «неверного пароля» и добейтесь одинакового времени ответа в обоих случаях (хешируйте фиктивный пароль, если пользователя не существует). Иначе форма входа превращается в API для перебора пользователей.

Сессии, cookie и CSRF

После входа сессионная cookie и есть пользователь, поэтому она заслуживает такой же защиты, как пароль. Основную работу выполняют три атрибута cookie и один префикс в имени:

  • Secure означает, что cookie никогда не передаётся по обычному HTTP, поэтому её нельзя перехватить в публичной сети Wi-Fi.
  • HttpOnly делает её невидимой для JavaScript, поэтому XSS-уязвимость не сможет её прочитать.
  • SameSite=Lax (или Strict для админ-панелей) не даёт отправлять её в межсайтовых POST-запросах, что нейтрализует большинство CSRF-атак.
  • Префикс __Host- заставляет браузер отклонять cookie, если у неё нет флага Secure, пути Path=/ или если задан атрибут Domain, поэтому поддомен не сможет её перезаписать.
http
Set-Cookie: __Host-session=9f1c7e2a...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=28800

Действия, изменяющие состояние, никогда не должны быть доступны через GET. Ссылку вроде /account/delete?id=42 может вызвать тег <img> на любой странице, которую посещает пользователь, какими бы хорошими ни были флаги ваших cookie.

CSRF: вторая линия защиты за SameSite

Межсайтовая подделка запроса (CSRF) заставляет браузер, в котором пользователь уже вошёл в систему, отправить на ваш сайт запрос, изменяющий состояние, с другой страницы. Cookie с SameSite останавливают типичный случай, но для каждого запроса, отличного от GET, сохраняйте вторую защиту: anti-CSRF-токен для сессии в скрытом поле или собственном заголовке либо проверку того, что заголовок Origin (или Sec-Fetch-Site) совпадает с вашим собственным источником. Большинство фреймворков (Django, Rails, Laravel, server actions в Next.js) делают это по умолчанию; ошибка — отключить защиту для API-эндпоинта, а затем вызывать этот эндпоинт из браузера.

Идентификаторы сессий, ротация и JWT

Генерируйте идентификаторы сессий минимум со 128 битами случайности, меняйте идентификатор при входе и при любом изменении привилегий, чтобы защититься от фиксации сессии, завершайте сессии после периода бездействия и дайте пользователям кнопку «выйти на всех устройствах», которая аннулирует сессии на сервере. JWT делайте короткоживущими (минуты, плюс refresh-токен, который можно отозвать), никогда не храните их в localStorage, где их может прочитать любой скрипт, и считайте утечку ключа подписи полноценным взломом, потому что теперь можно подделать любой токен, который им когда-либо был подписан.

Инъекции и XSS: проверяйте ввод, кодируйте вывод

Инъекции (A05:2025) и межсайтовый скриптинг — одна и та же ошибка в разных местах: данные от пользователя передаются интерпретатору (SQL, командной оболочке, LDAP-запросу, HTML-странице) так, будто это код. И исправление везде одинаковое: разделяйте данные и код и никогда не собирайте команду конкатенацией строк.

Параметризованные запросы вместо склейки строк

Для SQL используйте подготовленные выражения (prepared statements) или ORM, который их генерирует: тогда база данных воспринимает ввод как значение, которое никогда не может изменить структуру запроса. То же правило действует для команд оболочки (передавайте массив аргументов, а не строку), для фильтров NoSQL (отклоняйте объекты-операторы вроде {"$gt": ""} в пользовательском вводе) и для путей к файлам (нормализуйте путь и убедитесь, что он остаётся внутри нужного каталога).

javascript
// Vulnerable: the input becomes part of the query text
const rows = await db.query(`SELECT * FROM users WHERE email = '${email}'`);

// Safe: the driver sends the value separately from the query
const rows = await db.query("SELECT * FROM users WHERE email = $1", [email]);

// Python / psycopg: same idea
cur.execute("SELECT * FROM users WHERE email = %s", (email,))

Кодирование вывода останавливает XSS

Межсайтовый скриптинг возникает, когда текст, контролируемый пользователем, записывается в страницу без кодирования под тот контекст, в который он попадает. Современные шаблонизаторы (React и JSX, Vue, Jinja2, Blade, ERB) кодируют по умолчанию; сегодня XSS обычно появляется через обходные механизмы: dangerouslySetInnerHTML, v-html, |safe, innerHTML, а также при сборке URL или встроенных обработчиков событий из пользовательских данных. Кодируйте под конкретный контекст, а форматированный HTML санитизируйте специализированной библиотекой, например DOMPurify, а не регулярным выражением.

Куда попадают данныеКак кодироватьПример
Тело HTMLHTML-сущности< превращается в &lt;, " — в &quot;
Атрибут HTMLСущности для атрибутов в кавычкахВсегда заключайте значение атрибута в кавычки; кодируйте " и '
JavaScriptНе вставляйте данные в текст скрипта; передавайте их через атрибуты data- или JSON-блок <script type="application/json"></script> внутри строки превращается в \u003c/script\u003e
Параметр URLПроцентное кодированиеencodeURIComponent(value)
Значение CSSПолностью избегайте; выбирайте имена классов из белого спискаНикогда не подставляйте пользовательский ввод в style

SSRF, загрузка файлов и десериализация

Подделка запросов на стороне сервера (SSRF): если ваш сервер загружает URL, указанный пользователем (вебхуки, прокси для изображений, превью ссылок), атакующий может направить его на внутренние сервисы или на эндпоинт метаданных облака 169.254.169.254 и прочитать учётные данные. Сначала разрешайте имя хоста, отклоняйте частные и link-local диапазоны, отключайте редиректы и размещайте такие загрузчики в сегменте сети, из которого недоступно ничего внутреннего.

Загрузка файлов: проверяйте тип по содержимому, а не по расширению или Content-Type от клиента; ограничивайте размер; храните файлы вне корня веб-сервера или в объектном хранилище под случайным именем, которое генерируете вы; по возможности отдавайте их с отдельного источника с nosniff и Content-Disposition: attachment. Никогда не допускайте, чтобы загруженный файл оказался там, где веб-сервер будет его выполнять.

Десериализация и mass assignment: никогда не десериализуйте недоверенные данные в формате, который может создавать экземпляры произвольных классов (нативная сериализация Java, pickle в Python, unserialize в PHP, YAML с пользовательскими тегами); используйте JSON со схемой. Привязывайте тело запроса к явному белому списку полей, чтобы пользователь не мог отправить POST с "role": "admin" в модель, у которой случайно есть такой столбец.

Broken Access Control: риск номер один

Broken Access Control (нарушение контроля доступа) занимает первое место в OWASP Top 10 с 2021 года и сохраняет его как A01:2025. Это не одна ошибка, а привычка: при каждом запросе проверять, кто перед вами (аутентификация), и забывать проверить, к чему этому человеку можно обращаться (авторизация). Классическая форма — небезопасная прямая ссылка на объект (IDOR): GET /api/invoices/1042 работает для владельца счёта, а заодно и для любого, кто поменяет номер.

  • Запрет по умолчанию. Каждый маршрут требует явного правила, разрешающего доступ; отсутствие правила означает 403, а не 200.
  • Проверяйте на сервере, для каждого объекта. Спрятать кнопку в интерфейсе — не контроль доступа. Каждое чтение и каждая запись должны подтверждать, что текущий пользователь владеет конкретной записью или имеет к ней доступ, — в том числе в массовых эндпоинтах, экспорте и фоновых задачах.
  • Используйте неугадываемые идентификаторы там, где это помогает (UUID), но никогда не полагайтесь на них: скрытность — не авторизация.
  • Ограничьте CORS. Access-Control-Allow-Origin: * вместе с учётными данными или отражение любого Origin, который присылает запрос, отдаёт ваш API любому сайту, который посещает пользователь.
  • Отключите листинг каталогов, заблокируйте .git, .env, файлы резервных копий и конфигурации на уровне веб-сервера и не выставляйте админ-панели в публичный интернет — или закройте их MFA и белыми списками IP.
  • Ограничивайте частоту и логируйте ошибки авторизации. Всплеск ответов 403 в одной сессии — это идущая атака перебора.
javascript
// Express: ownership check on every object access
app.get("/api/invoices/:id", requireAuth, async (req, res) => {
  const invoice = await Invoice.findById(req.params.id);
  if (!invoice || invoice.ownerId !== req.user.id) {
    return res.status(404).end(); // 404, not 403: don't confirm the record exists
  }
  res.json(invoice);
});

Уровень 5: зависимости и цепочка поставок ПО

Большая часть кода в типичном веб-приложении — это код, который вы скачали, а не написали. Поэтому Software Supply Chain Failures впервые появились на позиции A03 в OWASP Top 10 2025, а эксплуатация уязвимостей в DBIR 2026 обогнала фишинг и украденные учётные данные. В сентябре 2025 года после фишинговой атаки на одного сопровождающего в chalk, debug и 16 других npm-пакетов, которые вместе скачивают более двух миллиардов раз в неделю, внедрили вредоносный код, ворующий криптовалюту; каждый проект, который в это окно установил непривязанную версию, получил его автоматически.

  • Коммитьте lockfile (package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, go.sum) и устанавливайте зависимости в CI через npm ci, чтобы сборки были воспроизводимыми, а новый upstream-релиз не мог проскочить без проверки.
  • Сканируйте постоянно: npm audit, pip-audit, bundler-audit, GitHub Dependabot или Renovate для обновлений и сканер контейнеров, например Trivy или Grype, для уровня ОС.
  • Откладывайте обновления, не связанные с безопасностью, на несколько дней (minimumReleaseAge в Renovate), чтобы отравленный релиз, как правило, успели отозвать до того, как вы его примете, но патчи безопасности применяйте немедленно.
  • Фиксируйте сторонние actions и скрипты на SHA коммита в CI и используйте Subresource Integrity (integrity="sha384-...") для каждого скрипта, который загружаете с публичного CDN.
  • Давайте CI минимальные привилегии: токены только для чтения там, где это возможно, короткоживущие учётные данные OIDC вместо долгоживущих секретов и права на публикацию только у задачи релиза.
  • Генерируйте SBOM (CycloneDX или SPDX) при сборке, чтобы ответить на вопрос «нас это затрагивает?» за минуты, когда появится следующая CVE масштаба Log4Shell.
bash
# Reproducible install + audit in CI
npm ci --ignore-scripts
npm audit --audit-level=high

# Pin a GitHub Action to a commit, not a floating tag
# uses: actions/checkout@v4                 <- can change underneath you
# uses: actions/checkout@<full-commit-sha>  # v4.2.2

# Subresource Integrity for a CDN script (hash from: openssl dgst -sha384 -binary lib.min.js | openssl base64 -A)
<script src="https://cdn.example.com/lib.min.js"
        integrity="sha384-<base64-hash>"
        crossorigin="anonymous"></script>

npm ci --ignore-scripts блокирует скрипты, выполняемые при установке, через которые запускается большинство вредоносных npm-пакетов; затем снова включите скрипты только для тех немногих пакетов, которым действительно нужен этап сборки.

Если у вас WordPress или другая CMS, те же правила относятся к плагинам и темам — именно с них начинается большинство взломов CMS. Обновляйте ядро и все плагины, удаляйте неиспользуемые и проверьте, что сканер может узнать о ваших версиях, с помощью инструмента Определение CMS.

Уровень 6: защита сервера (порты, патчи, секреты, резервные копии)

Уровень приложения ничего не значит, если сервер под ним принимает вход по паролю через SSH, держит базу данных на публичном порту или не обновлялся с момента установки. Лучшие практики для серверов скучны — и именно через их отсутствие проникают программы-вымогатели.

Закройте все порты, которые вы не обслуживаете

Публичному веб-серверу нужны открытые порты 80 и 443 и SSH только с вашего диапазона IP-адресов. Базы данных (3306, 5432, 27017), Redis (6379), Elasticsearch (9200), админ-панели и эндпоинты метрик должны слушать только localhost или частную сеть. После каждого изменения проверяйте сервер снаружи с помощью проверки портов: именно так его видит атакующий, а группы безопасности в облаке легко истолковать неправильно.

bash
# Ubuntu: default-deny firewall, allow only web + SSH from your office range
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp
sudo ufw enable   # check your own IP is in that range first, or you lock yourself out

# SSH: keys only, no root password login, in a drop-in that sshd reads first
printf 'PasswordAuthentication no\nPermitRootLogin prohibit-password\n' \
  | sudo tee /etc/ssh/sshd_config.d/00-hardening.conf
sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin) '   # what sshd will really use
sudo systemctl reload ssh

В Ubuntu и Debian sshd читает файлы из /etc/ssh/sshd_config.d/ раньше остальной части sshd_config, и для каждой настройки побеждает первое прочитанное значение. Поэтому команды выше пишут drop-in с именем 00-hardening.conf: правка ниже в основном файле проиграет drop-in облачного образа, где указано PasswordAuthentication yes. Проверьте результат через sudo sshd -T, прежде чем выходить из сессии. В проверке портов порт, который файрвол отбрасывает, показан как Фильтруется (нет ответа), а порт, на котором никто не слушает, как Закрыт: оба варианта нормальны, а исправлять нужно Открыт.

Патчи по расписанию, секреты — вне кода

Включите автоматические обновления безопасности для операционной системы, подпишитесь на рассылки по безопасности для вашей среды выполнения и фреймворка и считайте критическую CVE в любом компоненте, доступном из интернета, задачей на тот же день. Запускайте сервисы от непривилегированных пользователей, в контейнерах или в песочнице systemd, чтобы скомпрометированный процесс не мог читать всю машину.

Секретам (паролям от баз данных, API-ключам, ключам подписи) не место в репозитории, в слое Docker-образа или в клиентском JavaScript. Загружайте их из переменных окружения, которые подставляются при деплое, или из менеджера секретов, меняйте их, когда уходит кто-то, у кого был доступ, и сканируйте историю репозитория инструментом вроде gitleaks: однажды закоммиченный ключ скомпрометирован навсегда, даже если коммит удалён.

Резервные копии, которые переживут вымогателя, и CDN перед сайтом

Следуйте правилу 3-2-1: три копии на двух разных носителях, одна — за пределами площадки, причём хотя бы одна копия должна быть неизменяемой или офлайн, чтобы вымогатель, добравшийся до сервера, не смог зашифровать и резервные копии. Шифруйте резервные копии, включайте в них базу данных и каталог загрузок и, что особенно важно, регулярно проверяйте восстановление. Резервная копия, из которой никто ни разу не восстанавливался, — это надежда, а не план. От времени восстановления зависит, станет ли инцидент простоем или концом компании.

Поставьте перед origin-сервером CDN или WAF (Cloudflare, Fastly, AWS CloudFront с WAF или аналог вашего хостинга). Он поглощает объёмные DDoS-атаки, применяет управляемые правила против типичных инъекций и вредоносных ботов, скрывает IP origin-сервера и даёт место для rate limiting и гео-правил без правок в приложении. Затем ограничьте файрвол origin-сервера так, чтобы к порту 443 напрямую можно было подключиться только с IP-диапазонов CDN; иначе атакующие просто обойдут его. Проверка хостинга показывает, какой CDN стоит перед сайтом и что публичные данные говорят о хостинге за ним.

Аутентификация почты: SPF, DKIM и DMARC

Веб-безопасность включает и почту, которая доставляет письма сброса пароля, счета и ответы поддержки. Без SPF, DKIM и DMARC кто угодно может отправить письмо от имени billing@yourdomain.com; с февраля 2024 года Google и Yahoo требуют все три от массовых отправителей, а с мая 2025 года то же самое делает Microsoft для Outlook.com. Весь набор — три TXT-записи в DNS:

dns
yourdomain.com.                    TXT  "v=spf1 include:_spf.google.com -all"
google._domainkey.yourdomain.com.  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."
_dmarc.yourdomain.com.             TXT  "v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s"

DMARC p=reject — это ещё и защита бренда: только она не даёт фишинговой рассылке поставить ваш точный домен в поле From. Агрегированные отчёты (rua=) показывают, кто пытается это сделать.

Начните DMARC с p=none, чтобы собирать отчёты, затем переходите на p=quarantine и p=reject, когда все законные отправители (платформа рассылок, helpdesk, CRM) проходят выравнивание. Добавьте записи MTA-STS и TLS-RPT, чтобы принудительно шифровать доставку на ваши почтовые серверы. На доменах, которые никогда не отправляют и не принимают почту, опубликуйте null MX (MX 0 .), v=spf1 -all и запись DMARC с p=reject: null MX лишь сообщает, что домен не принимает почту, а подделки отклонять получателям позволяют SPF и DMARC. Проверьте каждую запись в проверке SPF-записи, проверке DKIM и проверке DMARC, а если доставляемость упала, проверьте свои IP-адреса отправки по спискам блокировок в проверке IP в чёрных списках.

Логируйте, мониторьте и готовьтесь к взлому

Две из десяти категорий OWASP 2025 посвящены тому, что происходит после ошибки: Security Logging and Alerting Failures (A09, сбои логирования и оповещений) и новая Mishandling of Exceptional Conditions (A10, неправильная обработка исключительных ситуаций). Типичный взлом по-прежнему остаётся незамеченным месяцами, потому что доказательства так и не были записаны — или были записаны, но никто их не смотрел.

  • Логируйте события безопасности с контекстом: каждый успешный и неудачный вход, изменения MFA, сбросы паролей, изменения прав, отказы в доступе, ошибки валидации ввода и действия администраторов — с меткой времени, ID пользователя, IP-адресом источника и user agent.
  • Никогда не логируйте секреты: пароли, сессионные токены, полные номера карт, API-ключи. Маскируйте их на уровне логирования, а не в каждом месте вызова.
  • Отправляйте логи за пределы сервера в централизованное хранилище (CloudWatch, Loki, Elastic, SIEM) со сроком хранения не менее 90 дней, чтобы атакующий с правами root не мог замести следы.
  • Настраивайте оповещения на шаблоны, а не на отдельные события: 50 неудачных входов за минуту, всплеск ответов 403 в одной сессии, новый администратор, созданный в нерабочее время, сертификат для вашего домена, который вы не запрашивали.
  • Закрывайтесь при сбое (fail closed): если зависимая проверка (сервис аутентификации, rate limiter, WAF) завершается ошибкой, отклоняйте запрос. Категория A10 существует потому, что очень многие системы предоставляют доступ, когда проверка, которая должна была его заблокировать, выбрасывает исключение.
  • Мониторьте снаружи: доступность, срок действия сертификатов, изменения DNS, попадание в чёрные списки и регрессии заголовков. Запускайте заново проверку SSL-сертификата, проверку HTTP-заголовков и проверку DNS домена (40 проверок делегирования, DNS-серверов, DNSSEC и SPF/DMARC) после каждого деплоя.

Напишите план реагирования на инциденты заранее

Заранее решите, кто дежурит, как сменить все учётные данные, как перевести сайт в режим только для чтения, где лежат чистые резервные копии и каких регуляторов или клиентов нужно уведомить и в какие сроки (72 часа по GDPR). Раз в год проводите учения (tabletop). Отрепетированный план превращает дни импровизации в чек-лист.

Опубликуйте файл security.txt по адресу /.well-known/security.txt (RFC 9116) со строкой Contact: и датой Expires:: это два обязательных поля (ключ Encryption: необязателен). Исследователи, нашедшие ошибку на вашем сайте, первым делом посмотрят там; без него они могут не найти, кому сообщить.

Проверяйте: сканеры, пентесты и непрерывный контроль

Каждую из мер выше можно проверить, и проверку стоит по возможности автоматизировать, чтобы она не теряла актуальность. Разумная лестница — от бесплатных и мгновенных проверок до периодических и платных:

ПроверкаИнструментКак часто
Конфигурация TLS, цепочка, срок действияПроверка SSL-сертификата, SSL Labs, testssl.shПосле каждого изменения сертификата или сервера; срок действия — ежедневно
Заголовки безопасности и CSPПроверка HTTP-заголовков, MDN HTTP ObservatoryПосле каждого деплоя (добавьте в CI)
Открытые портыПроверка портов, nmap снаружи вашей сетиПосле каждого изменения файрвола или облачных настроек
DNS, DNSSEC, аутентификация почтыПроверка DNS домена, Проверка DMARCЕжемесячно и после любого изменения DNS
Устаревшие поддоменыПоиск поддоменовЕжеквартально и при каждом выводе сервиса из эксплуатации
Зависимости с известными уязвимостямиnpm audit, Dependabot, TrivyПри каждой сборке
Уязвимости на уровне кода (SAST)Semgrep, CodeQLВ каждом pull request
Уязвимости работающего веб-приложения (DAST)ZAP, NucleiЕженедельно на staging
Авторизация и бизнес-логикаРучной пентест или программа bug bountyЕжегодно и после выпуска крупных функций

Сканеры хорошо находят инъекции, проблемы с заголовками и устаревшие компоненты и плохо — ошибки авторизации и бизнес-логики, поэтому заложите в бюджет хотя бы один ручной тест в год для всего, что работает с деньгами или персональными данными. Исправляйте в порядке доступности для атаки: первым делом — всё, что может эксплуатировать неаутентифицированный пользователь из интернета.

Чек-лист безопасности сайта

Скопируйте его в README проекта или в трекер задач. Каждая строка — одна из практик выше, сформулированная так, чтобы её можно было отметить как выполненную или невыполненную; последний столбец — доказательство.

УровеньПрактикаГотово, когда
ДоменRegistrar lock и MFA в аккаунте регистратора; автопродление включеноWHOIS показывает clientTransferProhibited; до окончания регистрации больше 12 месяцев
DNSЗона подписана DNSSEC; запись CAA перечисляет только ваши центры сертификацииdig +dnssec возвращает RRSIG; DS-запись есть в родительской зоне
DNSНет «висячих» записей CNAME или AКаждое имя хоста из поиска поддоменов указывает на ресурс, который вы контролируете
TLSТолько TLS 1.2+, предпочтительно TLS 1.3, отдаётся полная цепочкаПроверка SSL-сертификата: цепочка полная, TLS 1.0/1.1 отклоняются
TLSHSTS с max-age на год и includeSubDomains (preload по желанию)Проверка HTTP-заголовков: тест HSTS пройден
TLSПродление автоматизировано через ACME; срок действия отслеживаетсяcertbot renew --dry-run проходит; оповещение настроено за 14 дней
ЗаголовкиCSP (на основе nonce), frame-ancestors, nosniff, Referrer-Policy, Permissions-PolicyПроверка HTTP-заголовков: оценка A или A+
АутентификацияХеширование Argon2id или bcrypt; правила паролей NIST; проверка по базам утечекОдин хеш вычисляется 100–250 мс; нет правил состава
АутентификацияMFA доступна всем и обязательна для администраторов; поддерживаются passkeysВход администратора невозможен без второго фактора
АутентификацияДля эндпоинтов входа, сброса пароля и MFA действует rate limiting по IP и по учётной записиШесть быстрых попыток с одного IP: шестая возвращает 429
СессииCookie с префиксом __Host- и флагами Secure, HttpOnly, SameSite; ID меняется при входеАтрибуты cookie видны в DevTools; старая сессия недействительна после входа
Ввод данныхПараметризованные запросы везде; вывод кодируется по контексту; загрузки проверяются по содержимомуПоиск по коду не находит запросов, собранных из строк; XSS-пейлоады отображаются как текст
Контроль доступаМаршрутизация с запретом по умолчанию; проверка владения для каждого объекта; строгий CORSПовтор запросов с ID другого пользователя возвращает 404 или 403
ЗависимостиLockfile в репозитории; npm ci; аудит в CI; actions зафиксированы на SHA; SRI для скриптов с CDNСборка падает при CVE высокой критичности
СерверПублично открыты только 80 и 443; SSH только по ключам; автоматические обновления безопасности; секреты в env или vaultПроверка портов показывает 3306 и 6379 как «Закрыт» или «Фильтруется», и 22 тоже, если проверять не из вашего диапазона
Резервные копии3-2-1 с одной неизменяемой копией; восстановление провереноПоследняя успешная проверка восстановления — не старше 90 дней
ПочтаSPF -all, DKIM, DMARC p=reject, MTA-STSПроверка DMARC пройдена; агрегированные отчёты приходят
МониторингСобытия аутентификации и авторизации логируются централизованно; оповещения на шаблоны; внешний мониторинг доступности, сертификатов и DNSТестовое оповещение отправлено и получено
РеагированиеПисьменный план реагирования на инциденты; опубликован security.txt; проведены штабные ученияПлан пересматривался в течение последних 12 месяцев

Как это соотносится с OWASP Top 10:2025

OWASP Top 10 — самый цитируемый список рисков безопасности веб-приложений; в редакции 2025 года порядок изменился и появились две новые категории. Если клиент, аудитор или стандарт соответствия спросит, как вы закрываете эти риски, вот таблица соответствия практикам из этого руководства:

OWASP Top 10:2025Практики в этом руководстве
A01 Broken Access Control (нарушение контроля доступа)Запрет по умолчанию, проверки для каждого объекта, строгий CORS, закрытые админ-панели
A02 Security Misconfiguration (небезопасная конфигурация)Заголовки безопасности, HSTS, закрытые порты, удалённые заголовки с версиями, отключённый листинг каталогов
A03 Software Supply Chain Failures (сбои цепочки поставок ПО) — новаяLockfile, аудит, зафиксированные actions, SRI, SBOM, CI с минимальными привилегиями
A04 Cryptographic Failures (криптографические ошибки)TLS 1.2+ и 1.3, HSTS, Argon2id, управление секретами, шифрование резервных копий
A05 Injection (инъекции)Параметризованные запросы, кодирование вывода, CSP, проверка загружаемых файлов
A06 Insecure Design (небезопасное проектирование)Модель угроз для каждого уровня, закрытие при сбое по умолчанию, rate limiting на этапе проектирования
A07 Authentication Failures (ошибки аутентификации)Правила паролей NIST, проверка по базам утечек, MFA и passkeys, ротация сессий
A08 Software or Data Integrity Failures (нарушение целостности ПО и данных)SRI, подписанные и зафиксированные зависимости, безопасная десериализация
A09 Security Logging and Alerting Failures (сбои логирования и оповещений)Централизованное логирование, оповещения на шаблоны, внешний мониторинг
A10 Mishandling of Exceptional Conditions (неправильная обработка исключительных ситуаций) — новаяЗакрытие при сбое, единообразные ответы об ошибках, никаких трассировок стека для пользователей

OWASP ASVS (Application Security Verification Standard) превращает тот же материал в пронумерованный чек-лист для аудитов. Уровень ASVS L1 — реалистичная цель для любого публичного сайта; L2 — для всего, что обрабатывает персональные или финансовые данные.

Проверьте заголовки безопасности своего сайта за секунды

Бесплатная проверка HTTP-заголовков от DNS Robot показывает заголовки ответа любого URL ровно так, как их отправляет сервер, и оценивает заголовки безопасности тестами Mozilla HTTP Observatory — с исправлением для каждого недостающего. Без регистрации.

Источники

  1. 2026 Data Breach Investigations Report, Verizon
  2. OWASP Top 10:2025, OWASP
  3. Password Storage Cheat Sheet, OWASP
  4. SP 800-63B-4: Authentication and Authenticator Management, NIST
  5. Ballot SC-081v3: reducing certificate validity and data reuse periods, CA/Browser Forum
  6. HTTPS by default, Google
  7. RFC 9116: A File Format to Aid in Security Vulnerability Disclosure, IETF

Частые вопросы о безопасности сайта

Какие меры безопасности сайта самые важные?

Переведите сайт на HTTPS с современным TLS и HSTS, отправляйте строгие заголовки безопасности (прежде всего Content Security Policy), хешируйте пароли с Argon2id и защитите вход MFA и rate limiting, используйте параметризованные запросы и кодирование вывода, проверяйте права доступа на сервере для каждого объекта, своевременно обновляйте зависимости и фиксируйте их версии, закройте неиспользуемые порты и централизованно логируйте события безопасности. Начните с защиты домена и DNS, потому что всё остальное зависит от того, контролируете ли вы по-прежнему своё доменное имя.

Что такое OWASP Top 10 и актуален ли он в 2026 году?

OWASP Top 10 — поддерживаемый сообществом рейтинг самых критичных рисков безопасности веб-приложений. В редакцию 2025 года входят Broken Access Control, Security Misconfiguration, Software Supply Chain Failures, Cryptographic Failures, Injection, Insecure Design, Authentication Failures, Software or Data Integrity Failures, Security Logging and Alerting Failures и Mishandling of Exceptional Conditions. Это по-прежнему эталон, на который ссылается большинство аудитов, стандартов и клиентов.

Какие HTTP-заголовки безопасности должен отправлять каждый сайт?

Как минимум: Strict-Transport-Security, Content-Security-Policy (включая frame-ancestors), X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin и Permissions-Policy. X-Frame-Options: DENY — устаревший запасной вариант для frame-ancestors, а Cross-Origin-Opener-Policy: same-origin — недорогое дополнение. Уберите Server и X-Powered-By, чтобы сканеры не могли узнать версии вашего ПО.

Достаточно ли HTTPS, чтобы сайт был защищён?

Нет. HTTPS шифрует трафик между браузером и сервером, но никак не защищает от инъекций, нарушений контроля доступа, слабых паролей, уязвимых зависимостей или открытого порта базы данных. Это базовый уровень, и HSTS его закрепляет: однажды увидев заголовок, браузер больше никогда не отправляет на ваш домен обычный HTTP-запрос.

Как правильно хранить пароли пользователей?

Никогда в открытом виде и никогда с быстрым хешем вроде MD5 или SHA-256. Используйте Argon2id (19 МиБ памяти, 2 итерации) или bcrypt с коэффициентом сложности 10 или выше, с уникальной солью для каждого пароля, и подберите параметры так, чтобы один хеш вычислялся примерно 100–250 мс. Проверяйте новые пароли по базам утечек и требуйте MFA для привилегированных учётных записей.

Что такое Content Security Policy и нужна ли она моему сайту?

CSP — это HTTP-заголовок, который сообщает браузеру, из каких источников странице разрешено загружать скрипты, стили, фреймы и другие ресурсы. CSP на основе nonce с 'strict-dynamic' останавливает большую часть межсайтового скриптинга, даже если уязвимость для инъекции существует. Она нужна любому сайту, который выводит контент от пользователей или загружает сторонние скрипты; начните в режиме report-only и переключитесь на блокировку, когда в отчётах о нарушениях не останется проблем.

Как проверить безопасность сайта?

Сначала выполните внешние проверки: проверку SSL — на версию TLS, цепочку и срок действия; проверку HTTP-заголовков — на заголовки безопасности; проверку портов — на открытые сервисы; и проверку DNS домена — на DNSSEC и SPF/DMARC. Затем проверьте зависимости через npm audit или аналог для вашего стека, запустите ZAP на тестовой копии и вручную проверьте контроль доступа, повторяя запросы от имени другого пользователя.

Почему срок действия TLS-сертификатов сокращается?

CA/Browser Forum проголосовал за сокращение максимального срока действия сертификата с 398 дней до 200 дней (15 марта 2026 года), 100 дней (15 марта 2027 года) и 47 дней (15 марта 2029 года), потому что более короткие сертификаты ограничивают ущерб от украденного ключа или ошибочно выпущенного сертификата и снижают зависимость от отзыва. На практике это значит, что продление нужно автоматизировать через ACME.

Как часто нужно обновлять зависимости сайта?

Применяйте патчи безопасности сразу после выхода — в идеале в течение 24 часов для всего, что доступно из интернета, — и запускайте автоматический аудит при каждой сборке. Для обновлений, не связанных с безопасностью, надёжнее небольшая задержка в несколько дней, потому что скомпрометированные релизы пакетов обычно быстро отзывают; инструменты вроде Renovate могут выдерживать такую задержку автоматически.

Действительно ли атакуют небольшие сайты?

Да, постоянно, потому что атаки автоматизированы и не выбирают цели по размеру. Сканеры прощупывают каждый публичный IP-адрес в поисках открытых портов и известных уязвимостей CMS, а инструменты для credential stuffing пробуют утёкшие пароли в каждой найденной форме входа. Небольшой сайт ценен и как площадка для фишинга, спам-ретранслятор или плацдарм для атаки на более крупного клиента.

Об авторе

Shaik Vahid

Основатель DNS Robot. Вахид много лет работал в поддержке веб-хостинга: исправлял клиентам домены, DNS-записи, SSL-сертификаты и доставку почты. Он создаёт инструменты DNS Robot и пишет эти руководства по ошибкам, с которыми люди сталкиваются на самом деле.

Проверка HTTP-заголовков

Анализ HTTP заголовков ответа и оценка заголовков безопасности