Привет, Хабр! Я заметила, что в стартап-культуре и среди небольших команд бытует миф: «Мы слишком малы, чтобы нас атаковали». Я считаю, что сейчас это одно из опаснейших заблуждений, поэтому и решила написать статью, чтобы поделиться своими мыслями и некоторыми знаниями на эту тему.
Дело в том, что современный хакер редко целенаправленно сидит с лупой над вашим кодом. Гораздо чаще по вашей инфраструктуре бьют автоматизированные скрипты, которые сканируют миллионы IP-адресов в поисках известных уязвимостей в nginx, устаревшей версии jQuery или открытого порта. Ваш проект для них — единица, которую можно использовать для майнинга, создания ботнета или шифрования данных с последующим вымогательством.

Не знаю все ли читатели этой статьи слышали слово ботнет, поэтому для тех кто не знает: ботнет — это сеть из зараженных вредоносным ПО компьютеров, смартфонов и других устройств, которыми удаленно управляет киберпреступник и конечно же без ведома их владельцев. Эти устройства чаще всего используются для рассылка спама, проведения DDoS-атак, кражи личных данных или майнинга криптовалюты.
Я считаю, что безопасность — гигиена для любого, кто выкладывает код на всеобщее обозрение в интернет. И хорошая новость в том, что для маленьких проектов она может быть практически бесплатной, но требует дисциплины и правильного набора инструментов. Предлагаю в этой статье попробовать выстроить нашу оборону по трем ключевым рубежам: зависимости, веб-сервер и образы приложений. Причём выстроить её так, чтобы она не уступала Сицилийской, Французской или Каро-Канн из шахмат.
И так, давайте наконец перейдём ближе к делу.
❯ Рубеж 1: Управление зависимостями
Сейчас практический любой современный проект построен на горстке своих и тысячах чужих строк кода — зависимостях. Это серьёзная уязвимость log4j в Java-мире или проблемы в популярных npm-библиотеках. Один скомпрометированный пакет может поставить крест на безопасности всего приложения.
В чём заключается опасность? Ну, во-первых, вы теряете контроль над своим приложением и доверяете выполнение чужого, потенциально враждебного кода с теми же привилегиями, что и ваш собственный. Это чревато тем, что возможно появление случайного брака, причём не из-за того, что разработчики чужого кода что-то не так сделали, а просто из-за итоговой сложности кода. А иногда ведь бывают и преднамеренные диверсии, так например произошло с пакетом event-stream в npm, когда злоумышленник добавил вредоносный код, который воровал криптовалюту (кому интересно, могут прочитать про это здесь). Во-вторых, кражу данных никто не отменял. Многие библиотеки имеют доступ к данным вашего приложения. Вредоносный код может тихо и незаметно передавать конфиденциальную информацию (токены, пароли, персональные данные пользователей) на внешний сервер. В общем, список можно продолжать и дальше, но думаю и так понятно, что зависимости это серьёзные вещи, требующие усиленного внимания.
Теперь возникает логичный вопрос: как же быть с этой угрозой?
В качестве решения этой проблемы рекомендую использовать Dependabot ну или его аналоги. Многие сейчас задумаются над тем, что это вообще такое. Так вот Dependabo — это инструмент от GitHub, который помогает поддерживать актуальность зависимостей в вашем проекте, автоматически проверяя их на уязвимости и выпуская обновления через пул-реквесты. Он ежедневно заглядывает в ваши package.json, requirements.txt, Dockerfile и сверяет версии с базой известных уязвимостей (CVE).
Всё, что вам нужно это добавить в репозиторий простой конфигурационный файл .github/dependabot.yml.
version: 2
updates:
# Проверка обновления для npm каждую неделю
package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
# Образы в Dockerfile
package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
Если использовать этот чудо-бот, то каждую неделю он будет создавать пул-реквест с обновлением уязвимых зависимостей. Причём это будет не простым уведомлением о проблеме, а сразу готовым решением. Ваша задача будет лишь проверить, что обновление не ломает совместимость, и влить его.
Переходим к следующему оборонительному рубежу, который первым встречает атаки извне.
❯ Рубеж 2: Веб-сервер Nginx

Nginx — это высокопроизводительное программное обеспечение с открытым исходным кодом, которое может использоваться как веб-сервер, обратный прокси-сервер, балансировщик нагрузки, почтовый прокси-сервер и шлюз API
Ваше приложение, будь то Node.js, Python или PHP-скрипт, обычно стоит за веб-сервером Nginx. Именно он берёт на себя весь первоначальный внешний удар. Стандартная конфигурация хороша для старта, но она оставляет много окон, в которые можно заглянуть злоумышленнику. Так вот чтобы закрыть эти окна, нужно выполнить базовый харденинг, то есть усилить безопасность системы путём оптимизации её настроек.
Для начала следует сделать совсем простую вещь — скрыть версию Nginx. Как не странно, но Nginx по умолчанию любезно сообщает злоумышленнику свою версию в заголовках Server, а зная версию, легко найти подходящую уязвимость. Поэтому сразу прописываем:
server_tokens off;
После того как мы спрятали версию нашего Nginx, нужно позаботиться о защита от MIME-спуфинга. MIME-спуфинг — это метод кибератаки, при котором злоумышленник манипулирует метаданными файла, чтобы выдать его за другой, обычно менее опасный, тип контента.
И так, прописываем следующую строку кода:
add_header X-Content-Type-Options "nosniff";
Это заставляет браузер строго следовать объявленному типу содержимого и не пытаться угадать его. Теперь мы защищены от выполнения вредоносного скрипта.
Далее важно позаботиться об угрозе кликджекинга. Кликджекинг — это метод мошенничества в интернете, при котором злоумышленник обманом заставляет пользователя нажать на невидимый или замаскированный элемент на веб-странице. В результате пользователь непреднамеренно совершает действия, выгодные мошеннику.
Пропишем следующее:
add_header X-Frame-Options "SAMEORIGIN";
Заголовок X-Frame-Options запрещает отображать вашу страницу внутри <iframe> на другом сайте.
Ну и в завершении важно включить HSTS. Эта директива приказывает браузеру подключаться к вашему сайту только по HTTPS, даже если пользователь ввел http://. Это защищает от атак понижения уровня шифрования.
Для этого нужно прописать:
add_header Strict-Transport-Security "max-age=63072000" always;
Собрав всё вместе, наш второй оборонительный рубеж должен выглядеть следующим образом:
server {
listen 443 ssl http2;
server_name your-project.com;
server_tokens off;
# ... ваши обычные location и proxy_pass ...
# Заголовки безопасности
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "SAMEORIGIN";
add_header Strict-Transport-Security "max-age=63072000" always;
}
Наконец можем перейти к последнему рубежу обороны.
❯ Рубеж 3: Образы приложений
Современная разработка основана на использовании контейнерных образов, содержащих полные среды выполнения. Каждый образ представляет собой многоуровневую структуру зависимостей, где уязвимость в базовом слое распространяется на все производные образы.
Для борьбы с этими угрозами предлагаю использовать Trivy.

Trivy — комплексный сканер с поддержкой multiple payload analysis. Плюс в том, что его не нужно настраивать, он просто работает.
Trivy за секунды сканирует образ, находит все известные уязвимости в установленных пакетах и выдаст наглядный отчет с уровнем критичности (CRITICAL, HIGH, MEDIUM, LOW) и ссылками на CVE. Это похоже на Dependabot, но для всей операционной системы внутри контейнера.
Как встроить это в процесс? Думаю, что элегантным решением будет добавить этот шаг в ваш CI/CD-пайплайн (например, GitHub Actions). Тогда сборка и деплой будут прерываться, если обнаружена уязвимость уровня CRITICAL или HIGH. Это предотвращает попадание заведомо уязвимых образов в продакшен.
# Пример шага в GitHub Actions
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'your-username/your-small-project:latest'
exit-code: 1 # Падаем только если есть уязвимости
severity: 'CRITICAL,HIGH'
Как мне кажется: такой подход к безопасности проекта требует скорее дисциплины, чем чего либо ещё. Сделав эти проверки частью вашей ежедневной рутины, вы сможете защитить своей проект на ранних этапах разработки.
Надеюсь эта статья была полезной и интересной для вас. Спасибо за внимание!
Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud - в нашем Telegram-канале ↩