Привет, Хабр! Я заметила, что в стартап-культуре и среди небольших команд бытует миф: «Мы слишком малы, чтобы нас атаковали». Я считаю, что сейчас это одно из опаснейших заблуждений, поэтому и решила написать статью, чтобы поделиться своими мыслями и некоторыми знаниями на эту тему.

Дело в том, что современный хакер редко целенаправленно сидит с лупой над вашим кодом. Гораздо чаще по вашей инфраструктуре бьют автоматизированные скрипты, которые сканируют миллионы 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-канале 

Комментарии (0)