Мы серьёзно подходим к вопросам информационной безопасности наших продуктов: бережно относимся к пользовательским данным и разрабатываем сервисы с учётом требований информационной безопасности (ИБ) и публичных стандартов по разработке безопасных приложений. К сожалению, при этом всё равно могут встречаться уязвимости, создающие риски безопасности. Этого не нужно бояться, а лучше использовать эти знания для улучшения существующих контролей безопасности и построения новых. Для этого необходим видимый, эффективный и измеряемый процесс управления уязвимостями, который мы смогли построить в Ozon, и теперь хотим поделиться опытом, советами и граблями, на которые лучше не наступать.
Какие варианты организации процесса рассматривали и что выбрали?
Несколько лет назад, в начале пути, от процесса управления уязвимостями мы хотели простых вещей:
Унифицированного подхода к регистрации уязвимостей в наших сервисах.
Возможности в произвольный момент понять, есть ли неисправленные уязвимости в конкретном сервисе, и заодно посмотреть их историю.
Нам очень нравится методология OWASP SAMM — мы используем её как фреймворк для построения жизненного цикла разработки безопасных приложений (S-SDLC):
Среди разных практик ИБ, сгруппированных по бизнес-доменам, есть то, что нам интересно, а именно раздел Defect Management:
The Defect Management (DM) practice focuses on collecting, recording, and analyzing software security defects and enriching them with information to drive metrics-based decisions.
То есть фактически тогда мы решили сделать первый уровень зрелости:
Transparency of known security defects impacting particular applications
В качестве багтрекера командами разработки в Ozon используется великая и могучая Джира. Исходя из этого, мы внедрили первую версию процесса, добавив в каждый проект в Джире отдельный тип тикета «Уязвимость». Приоритет тикета показывает «Уровень опасности». Таким образом получилось выделить исправление уязвимости на фоне просто задач по разработке и исправлению багов.
В ходе первого года работы по такой схеме выяснилось, что:
Далеко не все руководители разработки были рады, когда инженеры ИБ заводили задачи в их проектах в Джире, предпочитая делать это самостоятельно.
Достаточно проблематично внедрить для определённого типа тикета («Уязвимость») единую схему ограничения доступа для всех проектов, коих у нас сотни.
Бывало, что и команда ИБ натыкалась на ограничения доступа при попытке заведения новых тикетов.
Не всегда сразу понятно, в каком проекте необходимо завести задачу по исправлению уязвимости, в то время как работа по ней уже ведётся и надо её где-то «приземлить». Например, для работы над исследованием потенциальной проблемы безопасности в рамках обработки репортов из нашей багбаунти-программы.
У нас для задач на разработку используется унифицированный общий процесс (workflow) для проектов, которые выкладывают свой код в продакшен (весьма удобно и, надеюсь, что мы расскажем об этом в будущих постах). Если кратко, то это обязательный набор статусов и переходов для тикетов в Джире, жёстко связанный с процессом CI/CD. Мы хотели иметь свой флоу в контексте исправления уязвимостей (в частности, убрать статус «Бэклог» и добавить специфичные статусы), и это могло стать проблемой.
Исходя из этого, мы приняли решение о переформатировании процесса в проект RISK. Таким образом, у команды ИБ появлялось гораздо больше возможностей по организации своего флоу в тикетах, ограничению доступа и т. д. Для руководителей разработки такой подход более привычен и удобен. Более того, наш процесс достаточно сильно похож на аналогичный процесс управления инцидентами и уже знаком целевой аудитории, что упростило его внедрение. Кстати, об этом процессе уже рассказал мой коллега в статье «Как упавший продакшен делает нас лучше».
Почему мы назвали проект RISK, а не, скажем, VULNS? На такой идентификатор нас натолкнул интересный доклад Дэниса Круза “Using JIRA to manage Risks and Security Champions activities” про принципы организации аналогичного процесса в Джире. В нём автор предлагает именно так назвать соответствующий проект. Получилось действительно лаконично и удобно в обращении:
— А вы уже завели риск?
— Вот же ж — RISK-2702!
Удачно совпало, что слово «риск» есть в русском языке, — и RISK-2702
читается и слышится всеми участниками процесса, включая представителей команд разработки, лучше, чем VULNS-2702
или VULN-2702
.
Помимо того, что RISK — это идентификатор проекта в Джире, есть и более объёмное толкование (на самом деле их несколько, но тут мы будем исходить из одного). Воспользуемся для этого документом OWASP Top Ten 2017: Application Security Risks, в котором наглядно показана схема риска безопасности:
Злоумышленники разными способами могут атаковать сервис и нанести нам ущерб. Это несёт риски безопасности, которые могут (или не могут) быть достаточно серьёзными и требующими внимания. Иногда эти способы легко найти и эксплуатировать, иногда — очень сложно. Аналогичная ситуация с возможным ущербом: его может не быть совсем или он может дорого стоить бизнесу.
Чтобы определить риски, необходимо оценить вероятности, связанные с источниками угроз, векторами атак и недостатками безопасности, а затем объединить их с оценкой технического и бизнес-ущерба для вашей компании. Сумма этих факторов определяет совокупный риск. Исходя из такого определения, мы начинаем понимать наш процесс уже не просто как регистрацию и исправление алертов от сканеров уязвимостей, результатов аудитов безопасности и т. д., но и как более бизнес-ориентированный процесс управления рисками безопасности.
Как внедряли?
Как уже упомянул выше, у нас был первый подход к процессу регистрации уязвимостей в виде отдельного типа тикета «Уязвимость» во всех проектах разработки. Мы в очередной раз обратились к разделу Defect Management из OWASP SAMM и наметили требования к будущему проекту и соответствующему процессу. Для перехода на RISK-и, мы сначала с помощью команды админов нашей Джиры завели соответствующий проект со специальным флоу и нужными нам полями. Нам было важно сохранить всю историю уязвимостей и пришлось мигрировать старые тикеты в новую схему — по сути, ретроспективно создавать RISK-и для уже закрытых уязвимостей).
В процессе управления уязвимостями нельзя забывать о его видимости для всех заинтересованных участников (разработчиков, менеджеров, QA-инженеров и руководителей направлений). Мы согласовали SLA по срокам исправления и подготовили внутренний лендинг про RISK-и на портале команды ИБ (именно на него ведёт ссылка крупными буквами «Описание проекта RISK» на примере тикета выше), где постарались максимально подробно и доходчиво рассказать о проекте для целевой аудитории (мы предпочитаем использовать формат карточек для оформления подобных текстов).
В Ozon проводится достаточно много внутренних митапов и регулярных больших встреч, включая All Hands — самую большую встречу для всех сотрудников. На ней мы и выступили с презентацией нашего проекта и таким образом постарались охватить максимальную аудиторию. У нас также есть свои каналы распространения информации, в частности внутренний канал #news-security, в котором мы делимся полезными ссылками и рассказываем про безопасность интересно и на человеческом языке. Впоследствии RISK-и начали уже сами распространять информацию о себе, как бы странно это ни звучало. По факту они стали очень важной точкой коммуникации между инженерами ИБ и командами сервисов.
Разобравшись с запуском RISK-проекта и его видимостью, мы взялись за автоматизацию процессов вокруг него:
RISK-тикет, как уже отмечалось выше, важная точка коммуникации инженеров ИБ с разработчиками, и необходимо, чтобы он был хорошо структурирован и понятен всем. Поэтому мы запустили бота, который стал присматривать за такими тикетами и ругаться, если что-то было плохо заполнено и ему не удалось это заполнить самому.
Нам было нужно автоматизированное форсирование сроков закрытия уязвимостей, и мы подготовили ряд мини-процессов, о которых пойдёт речь дальше.
Ну и наконец, какой же процесс без метрик и оценки его эффективности? В Defect Management этому посвящён второй поток "Metrics and Feedback". Мы в итоге разделили метрики на два класса:
Верхнеуровневые. Они показывают динамику появления, закрытия, сроков и т. п. RISK-ов по направлениям разработки.
Специфичные. Они фокусируют внимание пользователя на низкоуровневых показателях: типах уязвимостей, источниках информации (по сути, процессах ИБ), времени детекта и жизни уязвимости и т. д.
Для метрик первого класса мы подготовили специальный дашборд и начали проводить регулярные встречи с руководителями направлений, на которых обсуждаем как тенденции, так и конкретные интересные случаи.
Жизненный цикл RISK-а
Предположим, что нам пришёл очередной репорт в багбаунти-программу. Наш бот через API площадки багбаунти-программы (ранее это был HackerOne) узнаёт об этом и создаёт алерт в сервисе управления инцидентами Opsgenie. В последнем у нас настроены дежурства (с эскалацией и прочими плюшками). Дежурный подхватывает репорт и делает первичный анализ, включая проверку присланной уязвимости. Если репорт выглядит правдоподобно и в нём достаточно информации, то он переводится в соответствующий статус. Инженер ИБ заводит тикет в проекте RISK и берёт его в работу:
Подробно описывает суть проблемы, включая общее описание уязвимости, шаги для демонстрации (повторения атаки), описание потенциальных негативных последствий.
Указывает соответствующий ИБ-процесс в поле "Security Activity" (в данном случае как "Bug Bounty Program") и дополнительно — ссылку на репорт HackerOne. В последнем также указывается ссылка на RISK — таким образом мы получаем двухстороннюю связь. Остальные значения "Security Activity" и других полей RISK-а будут описаны ниже. Если кратко, то это процесс ИБ, который стал источником информации об уязвимости.
При необходимости ограничивает доступ к тикету с помощью поля «Уровень доступа».
Выставляет "Severity" тикета на основе экспертного заключения об уровне риска (в данном случае — об уровне опасности уязвимости). Он может быть скорректирован после обсуждения с представителем команды разработки сервиса.
Выставляет тип "Weakness" на основе справочника Common Weakness Enumeration (CWE).
А вот таким запросом в Джире можно найти все обнаруженные опасные уязвимости, которые могут привести к XSS-атакам на сервис конкретного направления, присланные через багбаунти-программу:
project = RISK AND Severity in (High, Critical) AND Weakness = 79 AND "Security activity" = "Bug Bounty Program" AND component = "Puschase and tools"
После создания RISK-а в Slack-канал #risk-of-the-day прилетает уведомление об этом, чтобы все заинтересованные могли следить за новым и интересным :)
Далее необходимо идентифицировать (микро)сервис, в котором была обнаружена уязвимость. Микросервисов у нас уже более 2800, так что это не всегда простая задача, для решения которой у нас есть отдельный алгоритм под названием «Поиск хозяев». В конечном итоге, мы находим сервис-виновник, а через единый справочник сервисов — соответствующую команду разработки. Идентификатор сервиса прописывается в специальное (и весьма полезное) поле "Service Name". Для чего оно используется, узнаете ниже в разделе про форсирование SLA по срокам исправления.
Потом мы призываем в тикет владельца сервиса (техлида) для обсуждения специфики проблемы, её системности, корректировки уровня опасности риска и необходимости (и возможности) исправления. По итогу в проекте соответствующего сервиса заводятся задачи на исправление — и связываются с RISK-ом, который назначаем на представителя сервиса и переводим в статус «Ожидает разработку». Параллельно с этим приходит наш бот и на основе ответственного за исправление выставляет для RISK-тикета компоненту, соответствующую конкретному направлению или команде.
Редко, но бывает и такое, что риск можно не исправлять (да-да, утверждение может показаться немного странным на первый взгляд в контексте статьи про информационную безопасность): риск уже компенсирован другими контролями ИБ и/или потенциальный ущерб достаточно незначителен. Примерами могут служить уязвимости вида self-XSS без явного вектора атаки или вывод незначительной отладочной информации в сообщениях об ошибках. В этих случаях RISK переходит на этап согласования принятия и, если всё нормально, то закрывается как «Согласованный».
Когда уязвимость исправлена (и закрыты прилинкованные тикеты в проекте), инженер ИБ всё проверяет и закрывает RISK-тикет как «Решённый».
В фоне жизненного цикла RISK-а происходят весьма интересные события.
— Присмотр за открытыми рисками. Тут инженер ИБ регулярно проходит по всем открытым RISK-ам, чтобы удостовериться, что они будут закрыты в срок:
если требуется дополнительная информация от ИБ, то предоставляет её,
если задача не взята в план работ, то договаривается об этом. У задачи должен быть срок исправления (“Due Date”),
если в задаче долгое время не было активности, то призывает в неё ответственного за продукт или соответствующую компоненту,
если работы по задаче закончены, то проверяет, исправлена ли уязвимость, и, если да, закрывает задачу.
— Форсирование SLA по срокам исправления уязвимостей (расскажу ниже подробнее про этот важный процесс).
— Регулярные встречи команды ИБ с руководителями разработки для анализа тенденций по RISK-ам в их направлениях:
сколько было выявлено рисков за последний месяц?
сколько исправлено?
как быстро исправляются риски?
и т. д.
Структура RISK-а
Давайте подробнее рассмотрим поля RISK-тикета. Помимо базовых, в нём есть специфичные поля, на основе которых потом делается аналитика.
Поле | Назначение |
Security Activity | Источник информации о проблеме безопасности: — Internal Security Audit (внутренний аудит безопасности силами команды ИБ), — External Security Audit (аудит силами внешнего подрядчика), — Bug Bounty Program (читайте нашу статью о запуске), — SAST (наши джобы по автоматизированному анализу безопасности кода), — DAST (сканирование сервисов на наличие уязвимости), — SCA (мониторинг уязвимостей в сторонних компонентах), — Threat Assessment (процесс моделирования угроз), — Secret Detection (поиск секретов в коде). |
Уровень доступа | Возможность временного ограничения доступа к специфичным рискам. — Public: выставляется по умолчанию. Доступ имеют все аутентифицированные в Джире пользователи. — Private: доступ к тикету имеют представители команды ИБ и те, кто в него добавлен. Ограничение временное, потому что мы исходим из открытости процесса и возможности обучения сотрудников на найденных проблемах. |
Weakness | Тип уязвимости, которая привела к риску. Используем список CWE List Version 4.4. |
Severity | Уровень опасности: Low, Moderate, High, Critical. Возможно, в будущем мы перейдём на отдельный от уязвимости CVSS Risk Score. Сейчас уровень опасности конкретного риска определяется субъективно инженером ИБ совместно с ответственным и с учётом специфики сервиса. Максимальные сроки исправления рисков безопасности: — High и Critical: до двух недель, — Low и Moderate: до одного месяца. |
Components | Направление, внутри которого находится соответствующая команда разработки сервиса. |
Found in Prod | Была ли уязвимость обнаружена в сервисе, который уже развёрнут в промышленном окружении. |
Потенциальный ущерб | Описание потенциальных негативных последствий в случае реализации угрозы. |
Application Type | Мы разделяем RISK-тикеты по типу приложения/сервиса, в котором была обнаружена уязвимость. Значения: — Frontend, — Backend, — Mobile. |
Service Name | В качестве идентификатора сервиса используется значение имени сервиса (оно уникально и обязательно для всех сервисов в Ozon). |
Форсирование SLA по исправлению RISK-ов
После того как риск безопасности подтвердился инженером ИБ и передан в работу, начинается отсчёт времени SLA исправления со стороны сервиса. Для того чтобы мы не выходили за пределы этих сроков используются следующие процессы:
Для критичного RISK-а мы заводим P1-инцидент (максимальный приоритет, см. статью «Как упавший продакшен делает нас лучше») — и время его починки исчисляется часами. Это очень мощный инструмент (эдакий BFG9000), который заставляет команду сервиса в буквальном смысле бросать всё и заниматься исправлением уязвимости. Нам приходилось применять его крайне редко, но хорошо иметь его в арсенале.
Бот ИБ регулярно приходит в «забытые» тикеты и уведомляет ответственного о том, что близится к окончанию или уже исчерпан срок SLA.
Проблемные тикеты эскалируются автоматизированно по следующему алгоритму:
при игнорировании тикета на протяжении трёх дней бот ИБ оповещает ответственного о наличии проблемы;
при очередном двухдневном игнорировании он подготавливает эскалацию до руководителя направления;
при дальнейшем однодневном простое эскалация происходит по тому же принципу с шагом в один день до CTO (никто не хочет, чтобы в его тикет пришёл CTO).
В Ozon есть единый для всех сервисов CI/CD-пайплайн релиза и выкладки в прод, что очень удобно. В нём уже есть несколько контролей ИБ, включая «Шлюз безопасности». В рамках этого важного и сильного контроля на основе поля "Service Name" происходит поиск незакрытых просроченных RISK-ов для соответствующего сервиса. Если таковые есть и они не исправляются в рамках этого релиза, то он блокируется.
Итоги и планы
Собрав определённое количество граблей, мы внедрили эффективный и измеряемый процесс управления уязвимостями на базе единого RISK-реестра:
Все уязвимости всех сервисов и приложений Ozon регистрируются в едином проекте RISK в виде тикетов, обогащаясь полезной информацией (уровень опасности, потенциальный ущерб, CWE, процесс ИБ, сервис и направление и т. д.).
На сроки исправления рисков действует SLA (они различаются в зависимости от уровня опасности). Они форсируются различными контролями, включая «Шлюз безопасности» на этапе выкладки новых версий сервисов.
У нас есть специализированный сервис агрегации метрик вокруг RISK-ов, и регулярный их анализ вместе с руководителями разработки.
Ответственный за исправление риска — хозяин сервиса либо руководитель направления. Команда ИБ помогает в оценке риска и подтверждении его закрытия, а также форсирует SLA по закрытию и эскалирует RISK-ки при его нарушениях.
Что мы хотим улучшить? Например, научиться считать и сделать отдельно дашборд с более специфичными метриками (Time-To-Detect, Window-of-Exposure с привязкой к процессу ИБ и т. д.) с их регулярным анализом. В итоге мы планируем прокачать наш процесс до максимального уровня в потоке "Metrics and Feedback" из OWASP SAMM: Defect Management.
P. S. Если у вас ещё нет процесса управления уязвимостями?
Я бы порекомендовал следующее:
Если у вас есть возможность, то сделайте в Джире проект RISK с минимальным набором специфичных полей. Начните с уровня опасности, описания ущерба и компонент по командам разработки. Регистрируйте там все новые уязвимости.
Расскажите всем разработчикам об этом процессе и задекларируйте SLA по исправлению.
Организуйте регулярный анализ найденных уязвимостей.
Иными словами, попробуйте реализовать первый уровень зрелости из OWASP SAMM: Defect Management — и у вас появится базовое понимание масштаба проблем безопасности и что с этим можно сделать.