Применение методологии Secure by Design для создания безопасных решений

изображение: DALL-E
Методология Secure by Design является важной составляющей в разработке современного веб-приложения. Несмотря на кажущиеся дополнительные трудозатраты на следование требованиям данной методологии, она может значительно сократить конечную стоимость разработки. Удешевление разработки достигается путем большего внимания к деталям в потенциально уязвимых участках системы не только на стадии самой разработки, но и на стадии постановки задачи и формирования бизнес-требований.
За счет такого подхода значительно снижается риск появления критических брешей в системе как в самой бизнес-логике, так и в технических деталях ее реализации. Для более детального рассмотрения подходов методологии Secure by design с точки зрения веб-приложения необходимо разделить процесс разработки на два основных этапа:
- Формулирование бизнес-процессов и подготовка ТЗ;
- Разработка приложения.
Формулирование бизнес-процессов и подготовка ТЗ
Общее описание
На первом этапе, наряду с функциональными требованиями к продукту, также должны закладываться свойства безопасности продукта. Это первый шаг для внедрения данной идеологии. Мы декларируем для всех участников разработки тот факт, что оценка безопасности того или иного решения в продукте будет являться важным этапом в общем процессе разработки. К тому же данное требование будет применимо и к формулированию бизнес-процессов. Каждый бизнес-процесс в техническом задании на разработку проходит оценку безопасности. Процесс анализируется представителями команды аналитиков и команды разработки. В случае наличия в штате организации специалистов в сфере ИБ привлечение их к оценке спроектированных бизнес процессов также может сильно поспособствовать обнаружению в процессах недетерминированных пограничных состояний или сайд-эффектов, которые могут позволить злоумышленникам использовать данные недоработки в своих интересах. Эффективность данного подхода можно рассмотреть на примере из практики.
Типовая ошибка в техническом задании
В качестве примера можно рассмотреть слегка утрированную ситуацию из реальной практики. Одним из частных функциональных требований для продуктов в сфере ИБ является наличие журнала, который фиксирует каждое действие любого пользователя в системе. При этом журнал действий очень быстро заполняется с течением времени, особенно при большом количестве пользователей в системе. Для этого нужно также предусмотреть ротацию данных. Данное требование было изложено в ТЗ продукта следующим образом: «Требуется реализовать ведение журнала действий пользователей в системе, в который должно попадать любое изменение состояния любого объекта в системе. Максимальное количество записей установить равным 1000000. Если достигнуто максимальное количество записей, то новые записи должны заменять собой самые ранние записи». К данному описанию приложена следующая схема бизнес-процесса.

Опытному специалисту сразу станет понятен недостаток такого подхода и легкость его обхода. После осуществления вредоносных действий в системе злоумышленник должен будет при помощи скрипта автоматизации выполнить 1000000 ничего не значащих действий для того, чтобы журнал был полностью перезаписан. Еще лучше это сделать другим пользователем, если есть такая возможность. Таким образом, непосредственно сами вредоносные действия и их автор будут удалены из журнала, из-за чего восстановление информации в исходное состояние будет сильно затруднено.
Для того, чтобы не допустить такой расклад еще на этапе проектирования бизнес-процесса необходимо предугадать ошибочность такого подхода и предложить альтернативные варианты решения. Таким решением может стать перенос более старых событий в отдельный буфер длительного хранения, либо пересмотр политики хранения записей. После внесения необходимых правок бизнес-процесс получил следующее описание: «Требуется реализовать ведение журнала действий пользователей в системе, в который должно попадать любое изменение состояния любого объекта в системе. Для каждой записи должно быть установлено обязательное время хранения в 3 месяца. По истечению этого срока запись может быть удалена. При достижении порога в 50 действий в секунду сессия пользователя должна быть заблокирована в системе». Тем самым мы исключили возможность переполнения журнала действий с целью удаления важных событий и заодно запретили пользователям осуществлять аномально большое количество действий.
Разумеется, не каждый бизнес-процесс можно привести к такому простому примеру. Чаще всего анализ процесса требует глубокого погружения в продукт, его задачи, цепочки процессов. Требуется понимание, для чего существует этот процесс, каковы сценарии его использования. Уделяется внимание его участникам, их ролям, входным и выходным данным и требованиям к ним. Только комплексный подход, включающий анализ всех аспектов программного продукта, может гарантировать качественную оценку безопасности процесса и обнаружение неудачных решений на стадии проектирования. Но, несмотря на большой объем проделанной на данной стадии работы, общая стоимость разработки может значительно сократиться за счет раннего обнаружения архитектурных дефектов, исправление которых на поздних стадиях разработки будет очень дорогостоящим, так как потребует не просто исправления багов в продукте, а переработки самого каркаса приложения – его базовых функций.
Разработка приложения
На первом шаге мы добились того, что у нас есть описание разрабатываемого нами продукта, которое было сформировано по методологии Secure by design. Вторым основным этапом в этом деле будет являться непосредственно сама разработка. На этом шаге нам потребуется углубиться в технические детали разработки Secure by design приложения. Предположим, что архитектурные и алгоритмические проблемы безопасности приложения мы отсекли на этапе проектирования. На этапе написания кода всем участникам команды разработки необходимо придерживаться технических рекомендаций по безопасной разработке, о которых мы подробнее поговорим ниже. Так как мы описываем идеологию Secure by design на примере веб-приложения, кратко рассмотрим типовую архитектуру подобных приложений.
Типовая архитектура веб-приложения
Чаще всего современные веб-приложения используют связку Single Page Application (SPA) + web API для взаимодействия с бэкендом. Логика отображения и взаимодействия с данными реализована на клиентской части (фронтенд), а для получения данных и выполнения действий над ними клиентская часть отправляет запросы к серверу по REST API или иному протоколу взаимодействия. В свою очередь бэкенд является «черным ящиком» для пользователя приложения. За границами REST API может скрываться любой стек технологий, используемых баз данных, сервисов и библиотек. Подобный подход уже делает приложение более безопасным, обеспечивая принцип Security through obscurity (безопасность через скрытие внутреннего устройства системы). Бэкенд может быть монолитным компонентом или разделен на множество микросервисов. Он может взаимодействовать с одной или несколькими базами данных, очередями сообщений и иными вспомогательными сервисами. Данные сервисы могут находиться как в одном контуре с самим сервером, так и во внешней сети. Схематично архитектуру типового веб-приложения можно представить на следующей схеме:

В данной архитектуре умышленно не уделено внимание вопросам масштабирования, балансировки нагрузки кластеризации баз данных и подобным вещам, характерным для высоконагруженных продуктовых приложений, так как в ином случае описание могло бы растянуться на сотни страниц текста. Предполагается, что описываемые далее подходы к безопасной разработке будут сохранять свою актуальность в более сложных production-архитектурах.
Фронтенд-разработка
Итак, первым рубежом технической безопасности нашего приложения является фронтенд. Код фронтенда выполняется в браузере клиента, открывшего нашу веб-страницу. Данный код, попавший на компьютер пользователя, может быть модифицирован и видоизменен самим клиентом, так как мы не можем повлиять на корректность его работы. Исходя из этого формируется основной постулат взаимодействия с фронтендом – мы никогда не можем ему доверять. Фронтенд не может единолично управлять таким функционалом как: разграничение доступа к компонентам веб-приложения, осуществление аутентификации или авторизации пользователей, валидация входных данных для передачи бэкенду и прочие функции, связанные с безопасностью приложения. Любые ограничения доступа к данным или функционалу на этом рубеже является серьезным нарушением безопасной разработки, так как любое подобное ограничение слишком легко обойти. На этом рубеже важно только защитить легитимного пользователя, но нет смысла защищаться от атакующего. Разумеется, клиентский код может работать с данными функциями для обеспечения согласованного визуала в зависимости от роли пользователя, факта его аутентификации и т.д. Но окончательное решение о доступности того или иного ресурса всегда должен принимать серверный компонент.
Несмотря на то что фронтенд-приложения всегда считаются недоверенным элементом, они все еще могут являться вектором атаки для некоторых типов уязвимостей. Самым популярным примером являются XSS-уязвимости, позволяющие злоумышленнику тем или иным способом разместить на сайте вредоносный js-скрипт, который будет выполняться у всех клиентов, зашедших на ту или иную страницу. Подобный скрипт может украсть куки и токены доступа, выполнять действия на сайте от имени пользователя, подделывать поля с чувствительными данными. Современная разработка веб-интерфейсов использует достаточно продвинутые фреймворки, такие как react, angular, vue, в которых изначально встроена защита от подобного рода уязвимостей. Тем не менее нельзя исключать в будущем обнаружение таких уязвимостей как в самих фреймворках, так и в дополнительных сторонних библиотеках, которые в достаточно больших объемах встречаются на любом современном сайте. К примеру, в 2020 году во фреймворке React и его экосистеме библиотек было найдено 4 XSS-уязвимости. Для команды фронтенд-разработки сформулировано требование, согласно которому требуется проверять используемые фреймворки и библиотеки в известных базах уязвимостей. В случае обнаружения требуется обновлять компонент на исправленную версию, либо отказываться от его использования.
Бэкенд-разработка
Вторым рубежом обороны от злоумышленника является бэкенд. В отличие от фронтенда данная часть веб-приложения функционирует в защищенной среде, на специально настроенном сервере или виртуальной машине. Возрастают как сложность для злоумышленника повлиять на работу этого компонента, так и требования безопасности к разработчикам и devops-инженерам.
Рекомендации по безопасной разработке серверной части включают в себя требования как к коду, так и к самой среде, на которой будут функционировать серверные компоненты. Перечислим самые базовые требования, которые чаще всего будут являться актуальными для любого продукта. В зависимости от специфики конкретного решения могут добавляться отдельные требования, которые должны обсуждаться отдельно еще на стадии проектирования.
К основному требованию к среде разворачивания серверных компонентов можно отнести защиту хостовой машины от несанкционированного доступа. Настройку системы должен производить квалифицированный инженер-devops согласно общепринятым решениям в данной отрасли. Они должны включать в себя закрытие лишних портов помимо тех, которые требуются для функционирования приложения. Если к серверу подразумевается возможность доступа по ssh, то для него должна быть отключена парольная аутентификация. В системе должна быть настроена корректная ролевая модель: каждый пользователь системы должен иметь минимальные привилегии, которых достаточно для осуществления характерных для его роли операций. Чувствительные данные, такие как пароли в других системах, криптографические ключи, сертификаты, файлы баз данных должны иметь максимально ограниченный доступ.
Со стороны разработки бэкенда приложения существует достаточно большой список ограничительных мер, позволяющих минимизировать риск наличия уязвимостей в своем продукте. Они касаются как сценариев взаимодействия по rest-api, так и особенностей написания кода. Перечислим основные требования к разработчикам:
Использовать только защищенный канал. Осуществлять сетевые взаимодействия с фронтендом и иными внешними компонентами возможно только по защищенному каналу. Взаимодействие без использования TLS/SSL давно ушло в прошлое, шифровать трафик нужно обязательно.
Обязательное использование механизма авторизации для работы с API. Взаимодействие с эндпоинтами сервера через API должно происходить только с использованием ключа сессии/токена доступа, либо иного безопасного компонента для идентификации и авторизации инициатора запроса. Любой токен должен верифицироваться на сервере, должны быть реализованы средства защиты от подделки. Механизм авторизации должен включать в себя ролевую модель, когда те или иные эндпоинты доступны для вызова только пользователям определенных ролей.
Защита от кражи токена доступа. Это тема для отдельной большой статьи, но суть данного требования в наличии в системе инструментов для проверки, что с данным токеном обращается именно тот пользователь, для которого он был выпущен. Для этих целей может использоваться user-agent запроса, слепок браузера, иные косвенные признаки пользователя, либо их комбинация.
Минимизация анонимных запросов и их защита. Большинство API не может быть реализовано без наличия одного или нескольких анонимных вызовов, которые не требуют от пользователя токена доступа. Самым частым примером такого вызова является вызов для входа в систему, принимающий на вход логин и пароль пользователя и возвращающий токен доступа для всех других эндпоинтов. Помимо этого, существуют и другие типичные функции, такие как регистрация в системе, восстановление пароля и т.д., которые не требуют от пользователя строгой идентификации себя при помощи пароля, либо средств двухфакторной аутентификации/авторизации. Количество таких анонимных точек входа в систему должно быть минимизировано. Также к ним должны применяться методы защиты, не позволяющие использовать данные вызовы злоумышленнику в своих интересах.
Рассмотрим простой пример: в системе есть функционал восстановления пароля пользователя по его почте. Для этого пользователь отправляет на сервер свой email, на что получает от сервера один из двух вариантов ответа:
- На почту отправлено письмо со ссылкой на восстановление пароля.
- Пользователь с такой почтой не найден в системе.
При этом пользователь, отправляя свою почту, никак не идентифицирует себя (он ведь забыл свой пароль), поэтому мы никак не можем определить легитимность такого запроса. Как это может использовать злоумышленник? Например, методом перебора определить email-адреса зарегистрированных в системе пользователей, ведь ответ сервера для существующих адресов может отличаться. Это может быть этапом разведки некой атаки, в ходе которой злоумышленник получает «живые» email-адреса, на которые, например, можно отправлять письма с вредоносным содержимым. Защитой от злонамеренного использования подобных анонимных вызовов может служить ограничение количества таких вызовов как глобально, так и для отдельных IP-адресов или подсетей. Другим вариантом может служить троттлинг, то есть умышленное замедление обработки такого вызова с целью серьезно замедлить попытки подбора.
Не хранить пароли в открытом виде. Наверное, в мире уже не осталось разработчиков, не знающих о том факте, что хранение паролей в открытом виде — это плохая практика. Тем не менее мы не можем знать наверняка, поэтому не лишним будет повторить это требование еще раз. К тому же надо понимать, что хранение захэшированных паролей тоже уже давно не является безопасным решением. Хэшировать нужно пару password + salt для исключения подбора пароля по радужным таблицам.
Никому не доверять. Любые входные данные, полученные из-за пределов зоны ответственности сервера, будь то фронтенд или какой-либо внешний компонент, должны быть тщательно проверены на корректность. Никогда нельзя исключать случаев изменений формата данных, объектной модели, а также злонамеренных действий. Поэтому работаем с данными только после их валидации. К этому же тезису можно отнести защиту от SQL-инъекций, когда злоумышленник будет пытаться встроить вредоносный SQL-код в одно из принимаемых сервером полей.
Минимальные права на доступы к базам данных. Различные компоненты бэкенда или микросервисы будут обращаться к базам данных для чтения или записи информации. При этом нужно понимать, что любой из этих компонентов в теории может быть атакован и выполнять в БД несанкционированные запросы. Для защиты от подобного рода проблем рекомендуется использовать ролевую модель, которая в той или иной степени присутствует в любой СУБД. Для каждого микросервиса должен создаваться индивидуальный пользователь, имеющий привилегии на чтение или запись только той информации, которая требуется ему для работы. Все остальные инструменты должны быть ему недоступны. Тем самым помимо неумышленных ошибок при разработке системы мы будем также защищены от потенциальных удачных SQL-инъекций, которые будут пытаться выполнить drop db в нашей базе или сделать что-то похожее.
Выводы
Таким образом, следование идеологии Secure by design требуется как на этапе проектирования продукта и описания бизнес-процессов, так и в процессе разработки программных решений. Разработка комплексных веб-приложений не может обойтись без серьезных проблем в случае пренебрежения рекомендациям безопасной разработки. Несмотря на кажущееся усложнение процесса, такой подход может ускорить и снизить затраты на разработку за счет минимизации времени на устранение допущенных ошибок на ранних стадиях проектирования и позволяет минимизировать риск репутационного удара в случае проведения успешной атаки на продукт.


