Безопасность веб-приложений: OWASP Top 10 с точки зрения защиты
OWASP Top 10 — это список наиболее серьёзных категорий рисков веб-приложений, который поддерживает сообщество OWASP. Его удобно читать как чек-лист для проектирования и ревью: по каждой категории есть понятные защитные практики. Эта статья написана с позиции защиты: мы не будем разбирать способы атак, а посмотрим, какие меры нужно внедрить на сервере srv-web01 и в самом приложении, как проверить, что они работают, и как заметить попытки обхода в журналах.
Категории и их порядок меняются от редакции к редакции, поэтому сверяйся с актуальной версией на сайте OWASP. Ниже мы сгруппируем меры по смыслу, а не по номерам. Пример окружения: Nginx на srv-web01 (192.168.10.20), приложение за ним, база данных во внутреннем сегменте 192.168.10.0/24.
Контроль доступа и аутентификация
Шаг 1. Проверка прав на сервере, а не в браузере
Нарушенный контроль доступа — самая распространённая проблема. Суть в том, что приложение проверяет права формально, например скрывает кнопку, но сервер не проверяет, имеет ли пользователь право на конкретный объект. Защита строится на нескольких правилах: запрет по умолчанию, проверка владельца объекта при каждом запросе, централизованная функция авторизации вместо разбросанных условий, непредсказуемые идентификаторы ресурсов там, где это уместно.
Практический приём — писать автоматические тесты на доступ: «пользователь А не может читать данные пользователя Б», «обычная роль не вызывает административные методы». Эти тесты запускаются в конвейере CI при каждом изменении. Обнаруживай нарушения по журналам: серия ответов 403 и 404 от одного клиента при переборе идентификаторов должна вызывать оповещение.
Шаг 2. Надёжная аутентификация и сессии
Используй проверенные библиотеки и стандарты, а не самописные механизмы. Пароли хранятся только в виде медленных хэшей с солью (argon2id, bcrypt, scrypt). Включай второй фактор для чувствительных ролей, ограничивай число попыток входа и добавляй задержки, не раскрывай, что именно неверно — логин или пароль. Идентификатор сессии должен быть длинным случайным, выдаваться заново после входа, иметь срок жизни и отзываться при выходе.
# Nginx: ограничение частоты запросов к странице входа
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
server {
location = /login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://127.0.0.1:8080;
}
}
Ограничение скорости не заменяет блокировку учётной записи, но сильно затрудняет автоматический перебор. Журналируй все события входа: успех, провал, смена пароля, включение и отключение второго фактора.
Работа с данными и вводом
Шаг 3. Инъекции: разделяй данные и команды
Инъекции возникают, когда пользовательский ввод попадает в запрос или команду как часть кода. Защита одна, и она универсальна: данные никогда не склеиваются с командой. Для баз данных это параметризованные запросы или ORM, для операционной системы — вызов программ с массивом аргументов без оболочки, для шаблонов — автоматическое экранирование вывода. К этому добавляется валидация ввода по белому списку: тип, длина, формат.
# Плохо: запрос собирается из строк
query = "SELECT * FROM users WHERE email = '" + email + "'"
# Хорошо: параметризованный запрос
cur.execute("SELECT id, name FROM users WHERE email = %s", (email,))
Учётная запись приложения в базе должна иметь только необходимые права: чтение и запись в свои таблицы, но не администрирование и не доступ к чужим схемам. Тогда даже при ошибке в коде ущерб ограничен. Для обнаружения подозрительных запросов включи на уровне WAF или журнала базы оповещения о синтаксических ошибках SQL и нетипично длинных параметрах.
Шаг 4. Защита данных при передаче и хранении
Весь трафик идёт по TLS, а старые версии протокола отключены. Включи HSTS, чтобы браузер всегда использовал HTTPS. Чувствительные данные шифруются при хранении, ключи вынесены из кода и репозитория, а ненужные данные просто не собираются: то, чего нет, не может утечь.
server {
listen 443 ssl http2;
server_name www.itpages.lab.local;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_certificate /etc/ssl/itpages/fullchain.pem;
ssl_certificate_key /etc/ssl/itpages/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
server {
listen 80;
server_name www.itpages.lab.local;
return 301 https://$host$request_uri;
}
Проверяй конфигурацию командой nginx -t и периодически тестируй качество TLS внешним сканером или утилитой testssl.sh на своих серверах.
Конфигурация и компоненты
Шаг 5. Заголовки безопасности и защита от XSS
Межсайтовый скриптинг предотвращается правильным экранированием вывода на стороне приложения. Браузерные заголовки добавляют вторую линию защиты. Политика Content-Security-Policy ограничивает источники скриптов, X-Content-Type-Options запрещает угадывание типа, а флаги cookie защищают сессию.
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'" 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;
server_tokens off;
Cookie сессии должны иметь флаги Secure, HttpOnly и SameSite=Lax (или Strict). Внедряй CSP сначала в режиме отчётов (Content-Security-Policy-Report-Only), чтобы увидеть, что сломается, и только потом включай блокировку. Для защиты от подделки межсайтовых запросов используй токены CSRF от фреймворка и проверку заголовка Origin.
Шаг 6. Безопасная конфигурация и минимальная поверхность
Многие инциденты — следствие небрежной конфигурации: включённый режим отладки, учётные данные по умолчанию, открытые панели управления, листинг каталогов, подробные сообщения об ошибках с трассировкой. Отдавай пользователю общее сообщение, а подробности пиши в журнал. Убери из публичного доступа служебные файлы, резервные копии и репозитории.
autoindex off;
location ~ /\.(?!well-known) { deny all; }
location ~* \.(bak|old|sql|swp|log)$ { deny all; }
error_page 500 502 503 504 /errors/generic.html;
Админка приложения не должна быть доступна из интернета: ограничь её адресами управляющей сети или вынеси за VPN.
location /admin/ {
allow 192.168.10.0/24;
deny all;
proxy_pass http://127.0.0.1:8080;
}
Шаг 7. Управление зависимостями и цепочкой поставки
Современное приложение на 80-90 процентов состоит из чужих библиотек, поэтому их уязвимости становятся вашими. Фиксируй версии зависимостей, веди перечень компонентов (SBOM), включи автоматическую проверку зависимостей в CI и обновляй их регулярно. Используй только доверенные источники пакетов и проверяй целостность по хэшам.
# Node.js
npm audit --omit=dev
# Python
pip install pip-audit && pip-audit -r requirements.txt
# Контейнеры и файловая система
trivy fs --severity HIGH,CRITICAL .
Результаты проверок включай в общий процесс управления уязвимостями, о котором мы писали ранее: у каждой находки должен быть владелец и срок. Не забывай про CMS и плагины: заброшенный плагин с известной дырой — типичная точка входа.
Обнаружение и реагирование
Шаг 8. WAF, журналы и мониторинг
Межсетевой экран веб-приложений (например, ModSecurity с набором правил OWASP CRS) отсекает типовой вредоносный трафик и даёт видимость. Начни с режима «только обнаружение», проанализируй ложные срабатывания, затем включи блокировку. WAF — это дополнительный слой, а не замена исправления кода.
sudo apt install -y libnginx-mod-http-modsecurity modsecurity-crs
# /etc/nginx/modsec/main.conf
SecRuleEngine DetectionOnly
SecAuditEngine RelevantOnly
SecAuditLog /var/log/nginx/modsec_audit.log
# после настройки исключений:
# SecRuleEngine On
Что должно попадать в журналы: аутентификация, отказы в доступе, ошибки валидации, изменения прав и конфигурации, административные действия. Не пиши в журнал пароли, токены и персональные данные. Отправляй логи на srv-scan01 или в отдельную систему сбора и настрой правила оповещения: всплеск ответов 401, 403, 500, необычные User-Agent, обращения к служебным путям.
Шаг 9. Защита встроена в разработку
Безопасность дешевле на стадии проектирования. Добавь в процесс разработки моделирование угроз на старте крупных функций, ревью кода с чек-листом, статический анализ (SAST) и проверку зависимостей в каждом запросе на слияние, а динамическое сканирование (DAST) запускай на тестовом стенде перед релизом. Отдельно проверяй приложение сканером из srv-scan01 только на своих стендах и в согласованное время. Для учебных целей полезно развернуть намеренно уязвимое приложение в изолированной сети без доступа к рабочим системам, чтобы команда училась распознавать ошибки в коде.
Шаг 10. Безопасная обработка загрузок, ссылок и внешних запросов
Две частые зоны риска — загрузка файлов пользователями и запросы, которые сервер выполняет по адресу, переданному клиентом. Для загрузок: проверяй тип по содержимому, а не по расширению, ограничивай размер, переименовывай файлы случайными именами, храни их вне корня веб-сервера или в отдельном хранилище и никогда не исполняй. Для серверных запросов по внешним адресам: разрешай только белый список доменов, запрещай обращения к внутренним диапазонам и служебным адресам метаданных, ограничивай схемы и перенаправления.
client_max_body_size 10m;
location /uploads/ {
root /srv/uploads;
add_header Content-Disposition "attachment";
add_header X-Content-Type-Options "nosniff" always;
location ~* \.(php|phtml|cgi|pl|sh)$ { return 403; }
}
Дополнительно на сетевом уровне запрети серверу приложений исходящие соединения во внутренние сегменты, кроме необходимых: так ошибка в коде не превратится в доступ к соседним системам.
Шаг 11. Проверка защиты и учения
Внедрённые меры нужно проверять. Составь для srv-web01 список контрольных проверок: заголовки безопасности на месте, TLS без устаревших версий, админка недоступна снаружи, режим отладки выключен, зависимости без известных критичных уязвимостей, журналы собираются и по ним срабатывают оповещения. Автоматизируй простые проверки скриптом и запускай после каждого развёртывания.
curl -sI https://www.itpages.lab.local | grep -iE "strict-transport|content-security|x-content-type|server:"
curl -s -o /dev/null -w "%{http_code}\n" https://www.itpages.lab.local/admin/
nginx -T 2>/dev/null | grep -E "server_tokens|ssl_protocols"
Раз в полгода проводи учения: смоделируй инцидент, например «в журнале всплеск ошибок 500 и необычные запросы», и пройди весь путь от оповещения до разбора и исправления. Это покажет, где не хватает журналов, прав или контактов.
Шаг 12. Культура безопасной разработки
Технические меры работают, когда их поддерживает привычка команды. Назначь в каждой группе разработки человека, отвечающего за вопросы безопасности (security champion), и выдели ему время. Введи короткие обучающие сессии на реальных ошибках из собственного кода, без поиска виноватых. Добавь в определение «готово» пункты о проверке прав, валидации ввода и журналировании. Храни перечень типовых безопасных решений, например готовый модуль авторизации и шаблон конфигурации Nginx, чтобы разработчикам было проще сделать правильно, чем неправильно. Принцип простой: безопасный путь должен быть самым лёгким. Когда защита встроена в привычные инструменты, а не навязана отдельным процессом, количество ошибок падает, а скорость разработки не страдает.
Частые ошибки и как их избежать
- Проверка прав только на фронтенде. Скрытая кнопка не защищает метод API. Любое действие проверяй на сервере и покрывай автоматическими тестами доступа.
- Ставка на WAF вместо исправления кода. Правила обходятся и дают ложное спокойствие. Используй WAF как слой обнаружения и временную меру, а причину устраняй в приложении.
- Забытые зависимости и режим отладки. Старый плагин, отладочная консоль и стандартные пароли остаются годами. Включи автоматическую проверку зависимостей, чек-лист релиза и регулярное сканирование конфигурации.
Что дальше
Защита строится из нескольких слоёв — вот соседние темы серии: