Блог CosySoft
Технологии

Литературное IT-обозрение: читаем книгу Тани Янки «Безопасность web-приложений»

Наш backend-разработчик Коля прочитал популярную книгу Тани Янки «Безопасность web-приложений» и заметил, что многие советы либо устарели, либо проходят в категории капитана Очевидность. Например, в книге присутствуют советы вроде «резервные копии бесполезны, если их нельзя использовать для восстановления данных», или «сделайте пароль подлиннее, чтобы он был безопаснее». В ответ на такие очевидные истины возникает закономерный вопрос — а применимо ли что-то из этого к реальным задачам разработки? Поэтому после прочтения Николай книгу не сжег, а выделил из прочитанного ряд полезных идей, дополнил это актуальными инструментами и объединил это все в статье.
По мере чтения книги меня все больше терзали вопросы: «Зачем это все?» и «Что это нам дает на практике?». На почти 450 страницах автор подробно рассказывает о важности базовых мер безопасности, но большая часть информации вызывает недоумение: советы повторяются, иногда по десять раз, а примеры порой настолько просты, что напоминают инструкции для новичков. Я постарался отфильтровать самое полезное из этой книги и дополнил проверенными инструментами, которые сам знаю и которыми пользуюсь.

CIA (Confidentiality, Integrity, Availability)

Первое, о чем говорит автор, — модель CIA (Confidentiality, Integrity, Availability). И нет, это не та кап-теорема, о которой знает каждый backend-разработчик. Если в кап-теореме (Consistency, Availability, Partition Tolerance) нужно делать выбор, жертвуя одной из характеристик для сохранения двух других, то здесь три принципа, к которым стоит стремиться одновременно. Янка рассматривает их как обязательные стороны безопасности, соблюдение которых важно для минимизации уязвимостей веб-приложения.
  1. Confidentiality (Конфиденциальность) — защита данных от несанкционированного доступа. Классический пример: если пользователь оставил в системе пароль, он не должен быть виден ни в каком формате, даже частично. Безопасные системы используют шифрование и избегают хранения критичных данных в открытом виде.
  2. Integrity (Целостность) — обеспечение точности и корректности данных. Данные не должны изменяться случайным образом или сторонними лицами. Например, при передаче транзакционных данных через API необходимо использовать проверенные методы цифровой подписи, чтобы быть уверенным, что данные не изменены «по дороге».
  3. Availability (Доступность) — данные и функции должны быть доступны пользователям, когда это нужно. Например, DDoS-атака может полностью парализовать сайт. Настроенные на этом уровне системы обеспечивают баланс нагрузки и защиту от перегрузок, что особенно важно для веб-приложений с высокой посещаемостью.
Для опытного разработчика такой подход вряд ли станет открытием. CIA-модель — полезная основа, но в реальных сценариях нам необходимы более детализированные подходы и примеры для конкретных случаев, которые действительно встречаются в веб-разработке, а не просто следование «прописным истинам».

Проектирование и моделирование угроз

Переходя к проектированию безопасного веб-приложения, Янка отмечает важность моделирования угроз. Идея в том, чтобы на этапе проектирования не просто учесть общие требования безопасности, а представить, какие угрозы могут возникнуть, исходя из характера приложения и потенциальных атакующих.
Безопасность на этапе проектирования
  1. Кто?
  2. Что?
  3. Зачем?
  4. Как злоумышленник может изменить данные для аутентификации?
  5. Что происходит, когда злоумышленник может прочитать данные профиля пользователя?
  6. Что происходит, когда доступ к базе данных профилей пользователя запрещен?
Моделирование угроз стало популярным инструментом среди архитекторов и безопасников, хотя, в реальности это больше напоминает хорошо структурированный мозговой штурм. В теории моделирование направлено на предсказание слабых мест в системе, когда группа специалистов оценивает потенциальные угрозы и продумывает защитные меры.

Одним из популярных подходов к моделированию угроз является методология STRIDE.

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

Проблемы безопасности по OWASP

Одним из центральных источников рекомендаций по безопасности веб-приложений является проект OWASP (Open Web Application Security Project), где регулярно публикуется список самых распространенных уязвимостей.
OWASP Top 10
  • нарушение контроля доступа;
  • недочёты криптографии;
  • инъекции;
  • небезопасный дизайн;
  • небезопасная конфигурация;
  • использование уязвимых или устаревших компонентов;
  • ошибки идентификации и аутентификации;
  • нарушения целостности программного обеспечения и данных;
  • ошибки логирования и мониторинга безопасности;
  • подделка запросов на стороне сервера.
Этот список часто упоминается в литературе по безопасности, и он действительно может быть полезен для разработчиков. Однако восемь из десяти проблем из OWASP Top 10 — это не атаки как таковые, а ошибки в разработке, которые делают приложения уязвимыми.
  1. Сломанный контроль доступа — проблема №1, и она связана не с внешними атаками, а с плохой настройкой доступа. Без надлежащего контроля доступом злоумышленник может получить несанкционированные права. Простая рекомендация — писать автотесты, которые проверяют как успешное, так и несанкционированное выполнение действий.
  2. Ошибки криптографии — тоже частая проблема. Здесь OWASP четко советует: «Никогда не пишите собственные криптографические функции». Большинство ошибок происходит при попытке «изобрести велосипед» или при использовании устаревших криптоалгоритмов. Если нужна безопасность, используйте проверенные библиотеки и алгоритмы, соответствующие современным стандартам.
  3. SQL-инъекции и другие виды инъекций — это известная проблема, когда неэкранированные пользовательские данные попадают в запросы к базе данных. Советы здесь просты: внедряйте валидацию и используйте ORM, чтобы минимизировать риски SQL-инъекций.
  4. Уязвимые компоненты — огромное количество современных приложений построено с использованием open-source библиотек и фреймворков, в том числе Node.js и других популярных решений. Но такие компоненты часто содержат уязвимости, иногда намеренно внедренные. Регулярное обновление библиотек и аудит зависимостей — базовые меры безопасности для минимизации риска.
Эти рекомендации могут показаться очевидными, но на практике именно их часто игнорируют в реальных проектах, что и становится причиной уязвимостей. OWASP предлагает ясные и проверенные подходы для решения этих проблем, что позволяет разработчикам быстрее и проще находить и устранять риски.

SQL-инъекции

SQL-инъекции — это классическая уязвимость, знакомая большинству разработчиков. Проблема возникает, когда параметры запроса вставляются в SQL-код без должной обработки, что позволяет злоумышленнику «встроить» свои команды и получить несанкционированный доступ к базе данных. Решение этой проблемы общеизвестно: передавайте параметры через защищенные методы, такие как подготовленные выражения (prepared statements), чтобы исключить возможность внедрения кода.
Однако SQL-инъекции — это лишь одна разновидность уязвимостей. Принципиально подобные инъекции могут возникать и в других типах команд, если пользовательские данные напрямую попадают в код. Например, популярным типом уязвимости является Command Injection, когда переданные аргументы используются для запуска системных команд.
В итоге, для предотвращения таких уязвимостей важно не только использовать подготовленные выражения в SQL, но и всегда экранировать или верифицировать ввод для любых вызовов, в которых пользовательские данные напрямую используются в системных командах или других интерпретируемых частях кода.

XSS — проблема фронтенда, но не только

XSS (Cross-Site Scripting) раньше был в списке топ-10 уязвимостей OWASP, но в последнее обновление 2021 года его исключили. Причина проста: XSS теперь чаще предотвращается на уровне фронтенд-фреймворков, таких как React, Angular или Vue. Эти фреймворки уже обладают встроенной защитой от внедрения вредоносного JavaScript-кода, и пока фронт развивается в их экосистеме, многие XSS-риски минимизируются.

Пример XSS-атаки

Представьте, что пользователь на веб-странице вводит свое имя. Вместо имени он может вставить вредоносный скрипт, например:
<script>alert("XSS атака!")</script>
Если ввод не проходит проверку, то каждый, кто откроет страницу с этим «именем», увидит всплывающее сообщение, вызванное встроенным кодом JavaScript. Это наглядно показывает, как уязвимость может легко повлиять на взаимодействие всех пользователей с веб-приложением.

Защита от XSS на бэкенде

Рекомендуется, чтобы бэкенд также принимал участие в защите от XSS и выполнял жесткую валидацию вводимых данных. Это снижает риск для пользователей в случае, если фронтенд не смог полностью предотвратить внедрение. Дополнительно бэкенд может экранировать символы или преобразовать подозрительный текст в Base64, чтобы гарантировать, что JavaScript-инъекция не будет выполнена.

Дополнительные меры для большого количества пользовательского HTML-контента

В приложениях, где пользователи часто добавляют HTML-контент, XSS-атаки могут быть сложнее предотвратить. В таких случаях стоит использовать специализированные библиотеки для безопасного рендеринга HTML, такие как DOMPurify, которые экранируют опасные теги и атрибуты, или разрабатывать специальные валидаторы. Это особенно полезно в приложениях с большим объемом пользовательского контента, где ручная валидация и проверка данных практически невозможны.

SSRF (Server-Side Request Forgery) и CSRF (Cross-Site Request Forgery)

SSRF — серверная угроза

SSRF-атаки (Server-Side Request Forgery) возникают, когда веб-приложение позволяет пользователям загружать ресурсы по ссылке. Злоумышленник может подменить внешний URL на внутренний серверный адрес, получив доступ к закрытым ресурсам или данным. Например, если приложение поддерживает загрузку изображений по URL, то вместо изображения злоумышленник может передать ссылку, ведущую на внутренний API компании, с целью сбора конфиденциальной информации.
Для защиты от SSRF важно:
  • ограничить разрешенные адреса и проверять ссылки;
  • ограничивать доступ к внутренним сервисам через сетевые правила и фильтрацию;
  • использовать списки доверенных доменов, с которых можно загружать ресурсы.

CSRF — защита от подделки межсайтовых запросов

CSRF-атака направлена на то, чтобы злоумышленник мог выполнять действия от лица пользователя без его ведома. Например, если приложение использует сессии в cookie, браузер автоматически подставит их к запросам, выполненным со стороннего ресурса. Так можно получить доступ к данным без аутентификации.
CSRF-токены — основной способ защиты:
  1. Stateful токены — хранятся на сервере и сравниваются с токеном из запроса. Такой подход требует обработки токенов на стороне бэка.
  2. Stateless токены с шифрованием — не требуют хранения на сервере. В токен можно записать уникальные идентификаторы и метку времени, а затем зашифровать, чтобы проверить аутентичность при каждом запросе.
  3. Double Submit Cookie — токен отправляется и в заголовке, и в cookie. Браузер не может отредактировать токен, переданный в заголовке, а сервер сравнивает значения.
Дополнительно на стороне фронтенда можно использовать SameSite атрибут для cookie или вводить подтверждения действий пользователей в стиле двухфакторной аутентификации.
Под конец в книге все же оказались полезные моменты, в том числе некоторые методы для выявления уязвимостей, о которых даже опытные разработчики могут слышать впервые. Но тут, как и раньше, информация в книге скудна, так что пришлось дополнительно разбираться самостоятельно. Давайте кратко пройдемся по этому.

Software Composition Analysis (SCA)

Этот метод проще всего понять и использовать, поскольку он занимается анализом состава используемого ПО — зависимости, библиотеки и их уязвимости. На уровне бэкенда с этим справляются многие IDE, и GitLab предлагает встроенные решения для анализа зависимостей. Из специализированных инструментов выделяется BlackDuck, который обнаруживает как уязвимости, так и проблемы с лицензиями. Dependency-Tree-анализ, шутливо обозначенный как "Dependency 3", тоже помогает выявить проблемные зависимости.

Unit-Тесты и интеграция САА

Юнит-тесты могут помочь реализовать базовый анализ, например, обеспечивая проверку ключевых зависимостей. Хотя в книге об этом почти не упоминается, интеграция юнит-тестов с SCA-инструментами помогает автоматизировать процесс выявления уязвимостей в библиотеках и делает код более защищенным с минимальными усилиями.

Фаззинг

Фаззинг (англ. fuzzing или англ. fuzz testing, буквально «испытание пушинками/волосинками», от англ. fuzz - с изначальным значением «делать неопрятным», «с прилипшими волосками», затем переносным «затуманивать», «путать»
Фаззинг — это метод тестирования, при котором проверяются различные варианты ввода параметров, чтобы выявить уязвимости в приложении. Например, если у вас есть параметр, ожидающий целочисленное значение, вы можете протестировать его на:
  • отрицательные значения;
  • большие отрицательные значения;
  • ноль;
  • положительные значения;
  • границы типа (IntMin, IntMax и т.д.)

Пример использования

Для реализации фаззинга можно использовать библиотеки, такие как JFuzz (если говорить о Java). Она интегрируется с JUnit 5 и упрощает процесс тестирования различных входных параметров.

Сравнение с PITest

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

SAST и DAST

SAST (Static Application Security Testing) и DAST (Dynamic Application Security Testing) — это два подхода к анализу безопасности приложений, каждый из которых имеет свои особенности.

SAST (Статический анализ)

SAST представляет собой метод анализа исходного кода без его выполнения. Это позволяет выявлять уязвимости на ранних этапах разработки. Примером инструмента для статического анализа является SonarQube, который интегрируется в процесс сборки и предоставляет отчеты о потенциальных проблемах в коде.
Преимущества SAST:
  • Выявление уязвимостей до запуска приложения.
  • Обнаружение проблем, связанных с кодом, такие как SQL-инъекции, неправильное использование API и т.д.

DAST (Динамический анализ)

DAST, с другой стороны, анализирует приложение во время его выполнения. Это позволяет тестировать приложение в реальных условиях эксплуатации. Примером инструмента для динамического анализа является OWASP ZAP (Zed Attack Proxy). Вы можете запустить его в виде Docker-контейнера и указать URL вашего приложения для анализа.
Преимущества DAST:
  • выявление уязвимостей, которые могут возникнуть только во время выполнения (например, XSS);
  • проверка заголовков HTTP и других аспектов конфигурации веб-приложения.

Применение в разработке

Хотя SAST и DAST могут быть полезны для разных этапов разработки, DAST чаще применяется на заключительных стадиях, когда код уже написан и развернут. Это особенно важно для обеспечения безопасности перед запуском приложения в продакшн.
Оба метода являются неотъемлемой частью процесса обеспечения безопасности приложений и должны использоваться в комплексе для достижения наилучших результатов.

IAST и RASP

IAST (Interactive Application Security Testing) и RASP (Runtime Application Self-Protection) — это более современные подходы к обеспечению безопасности приложений, которые предлагают дополнительные возможности по сравнению с традиционными методами SAST и DAST.

IAST (Интерактивное тестирование безопасности приложений)

IAST сочетает в себе элементы статического и динамического тестирования, обеспечивая более глубокий анализ. Он работает внутри приложения и в реальном времени анализирует код, потоки данных и конфигурации. Это позволяет выявлять уязвимости во время выполнения приложения, что делает IAST более точным и эффективным в обнаружении проблем.
Преимущества IAST
  • Возможность комбинирования статического и динамического анализа.
  • Более точная диагностика проблем в контексте работы приложения.
  • Поддержка непрерывной интеграции и доставки (CI/CD).

RASP (Защита приложений во время выполнения)

RASP — это подход, который обеспечивает защиту приложения во время его работы. Он интегрируется в приложение и может предотвращать атаки в реальном времени, анализируя входящие запросы и поведение приложения. Это делает RASP мощным инструментом для обеспечения безопасности в рабочей среде.
Преимущества RASP
  • Защита приложений на уровне выполнения, что позволяет предотвращать атаки в реальном времени.
  • Включение функций мониторинга и анализа поведения приложения.
  • Возможность обнаружения и блокировки атак, таких как SQL-инъекции и XSS, до того, как они смогут нанести вред.

Доступность и стоимость

Как вы заметили, большинство решений для IAST и RASP часто являются платными. Например, инструмент Contrast Security предлагает 30-дневный триал, но затем требует оплаты. К сожалению, многие из таких решений могут не поддерживать оплату из России, что ограничивает доступность для разработчиков и компаний в стране.

Заключение и дополнительные материалы

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