Через тернии к звёздам: строим SSDLC на OpenSource-компонентах

fed8e25e3547581b2dd87bf3e50c786d.jpg

Привет! Меня зовут Максим Коровенков, я DevSecOps Lead в СберМаркете. 

Хочу рассказать о том, как мы строим developer-центричный DevSecOps. Мы набили по ходу этого «строительства» уже достаточное количество шишек, поэтому, опыт, которым можно поделить, имеется в избытке.

Статья будет полезна тем, кто только начинает строить DevSecOps-процессы в компании, а также тем, кто уже начал, но столкнулся с проблемами роста (рост числа микросервисов, команд разработки, экспоненциальный рост нагрузки, нехватка кадров и т.д.). Поехали!

Немного контекста

Если мы рассмотрим целиком весь процесс безопасной разработки ПО (SSDLC), то увидим, что каждый из этапов содержит свои контроли безопасности (как на картинке ниже). В рамках данной статьи  я сконцентрируюсь только на автоматизации статического анализа исходного кода. 

c586a9c26e59421068779cde1de8c39d.png

Когда мы говорим о статическом анализа, как правило, подразумеваем 3 типа проверок:  проверка на наличие уязвимостей, секретов и уязвимых библиотек. Это нам будет важно далее при выборе набора инструментов для сканирования. 

Интегрируются данные типы проверок как правило в конвейер разработки (пайплайн), и на это есть несколько причин:

  1. Проверка каждого коммита. Во многих компаниях, как и в СберМаркете, применяются гибкие подходы в разработке, что подразумевает большое количество релизов в день/неделю. За всеми вручную невозможно уследить. При этом нам важно проверить каждую строчку кода перед выкладкой в продовое окружение, чтобы минимизировать вероятность появления уязвимостей.

  1. Повышение скорости обратной связи разработчикам. Необходимо как можно раньше сообщить разработчику о возможной проблеме с ИБ, чтобы  сделать как можно дешевле ее исправление.

Дано

Любая задача имеет некоторые исходные данные, и наша не исключение. Вот что мы имели на момент до начала построения DevSecOps-процессов (3 года назад):

  • языковой стэк — Ruby, Go, Python и JS/TS (React);  

  • SCM и CI — GitLab/GitLab CI;

  • распиливаемый монолит на Ruby;  

  • микросервисная архитектура;  

  • гибкая модель разработки по Agile/Scrum;

  • ненастроенные пайплайны безопасности.

Разумеется, в какой-то момент появилась потребность в том, чтобы заняться безопасностью, снизить риски ИБ и, как частный случай, начать внедрять сканеры в пайплайны разработки.

Поиски решения: почему OpenSource?

Первые вопросы, который возникают при желании внедрить сканеры в пайплайны: «Какой сканер лучше? Как его выбрать из огромного многообразия на рынке? OpenSource или коммерческое решение?»

 Тут не будет никакой интриги — выбор пал на OpenSource-инструменты и вот почему:  

  1. Социальный рейтинг инструментов. Можно ли вообще объективно оценить инструмент и его рейтинг относительно остальных? В целом да, приглашаю в тиндер из мира разработки — звездочки в github. Это вполне показательный индикатор — люди просто так добавлять проекты в избранное не станут. Помимо этого можно обратить внимание на количество контрибьюторов, дату последнего релиза и прочие открытые данные по проекту, чтобы понимать, активно ли он разрабатывается/поддерживается и пользуется ли он популярностью. Все это важные факторы до того, пока вы сами не протестировали инструменты и не сделали свои выводы.

    В коммерческих решениях вы можете полагаться только на искренность в глазах у интегратора или вендора, а также на отзывы о продукте от коллег или лидеров мнений. Мнение тех, кому доверяешь, — тоже может быть важным индикатором, но, засчет масштаба выборки, звёздочки всё-таки кажутся более репрезентативными.

  2. Размер комьюнити с огромной базой знаний по нюансам внедрения каждого из инструментов. С OpenSource-сканерами просто, поскольку, вероятно, любая техническая или внедренческая проблема, с которой можно столкнуться, уже была решена кем-то другим ранее. Поэтому для решения практически любой проблемы достаточно будет заварить чай, зайти в GitHub Issues и изучить чужой опыт.

    С коммерческими решениями все работает не так. Объективности ради нельзя сказать лучше это или хуже, но за ваши деньги вопрос внедрения инструмента и чтения GitHub Issues будет просто не вашей головной болью.

  3. Соответствие DevSecOps-подходам. OpenSource-инструменты чаще соответствуют DevSecOps-подходам — как правило речь ведется о легковесных инструментах, упакованных в Docker-образы, которые легко интегрируются в пайплайны, достаточно быстро работают, генерируют отчет в любом удобном машиночитаемом формате (sarif/json) и предоставляют весь необходимый функционал для управления сканированием.

    Если говорить о коммерческих решениях, они часто имеют сложную архитектуру и требуют установки серверной части. Лицензии продаются то по количеству ядер (потоков выполнения), то по используемым языкам программирования, то по количеству пользователей. Ограничение по количеству потоков сканирования, например, для нас является неприемлемым искусственным ограничением — так ИБ ожидаемо окажется «узким местом» (так переводится bottleneck, ничего личного) в процессе разработки.

  4. Коммерческие продукты больше не про улучшение качества сканирования, а про «рюшки». В ходе пилотирования различных коммерческих решений мы пришли к простому выводу: вендоры концентрируются на помощи в автоматизации рутинных задач, интеграциях, красивых дашбордах, но не на улучшении качества сканирования. Только зарубежные решения «первого порядка» — самые дорогие из правого верхнего квадранта Gartner —  демонстрируют результат сканирования ощутимо лучше OpenSource-инструментов. Подвох в том, что стоимость таких инструментов «окупается» только для международных IT-гигантов. А мы стараемся строить эффективное IT.

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

Сканирование на наличие секретов 

trufflehog3
gitleaks

Сканирование на наличие уязвимостей 

semgrep
gosec
bandit
brakeman
eslint

Сканирование на наличие уязвимых зависимостей

dependency track
trivy

Система управления уязвимостями

defectdojo

Итак, настало время эти инструменты внедрять!

Классический подход: попытка №1

Сначала мы пошли по «классическому» пути внедрения инструментов, так как именно такой подход описан во всех немногочисленных книгах по DevSecOps и казался самым логичным. Этот подход можно представить в виде трех простых тезисов:  

  1. Внедряем OpenSource-сканеры в пайплайны.

  2. Внедряем систему управления уязвимостями для сбора и триажа файндингов от сканеров. В нашем случае DefectDojo (и в вашем скорее всего тоже). 

  3. Реализуем триаж на стороне AppSec-ов. 

Как выглядела схема выполнения сканирования в пайплайнах разработки:

8c9416d0d3a19d299c712bc25b3430f1.png

Зелёная область — классический пайплайн разработки. В терминах GitLab этот пайплайн называется основным, или upstream-пайплайном. В нём помимо прочих выполняется так называемая trigger job, которая инициирует дочерний пайплайн (иначе downstream-пайплайн), который обозначен желтым. 

В downstream-пайплайне выполняются все необходимые проверки безопасности:

  • SAST (сканирование на наличие уязвимостей);

  • Secrets (сканирование на наличие секретов);

  • SCA (сканирование на наличие уязвимых зависимостей).

Далее результаты сканирования, собранные в виде артефактов, отправляются в DefectDojo. И только в фиолетовой области AppSec-и получают результат сканирования и выполняют триаж.

Стоит отметить, что в нашей первой попытке upstream-пайплайн не дожидался выполнения проверок безопасности. Это было сделано для того, чтобы никаким образом не влиять на Time to Market. 

Пара слов про роль системы управления уязвимостями в данном подходе: она является центральным звеном и выполняет ключевую роль в построении процессов управления уязвимостями, поскольку именно в там выполняется автоматизированный анализ файндингов (дедупликация) и ручной анализ (триаж). Данная система является единственным источником правды о наличии/отсутствии проблем безопасности в исходном коде всех сервисов, для которых были собраны результаты сканирования.

Так как изначально наша стратегия была следующей: просканировать сервисы, получить результаты, отправить их в DefectDojo и затриажить в ней, следовательно то, как выглядел downstream-пайплайн, мы сочли неважным. Соответственно выглядел он как набор job с неизвестными для разработчика названиями:

ac504e77f3075064603b6b92ef04f8c7.png

На скриншоте мы видим job с названием init, в которой выполнялась подготовка структуры данных для сохранения результатов сканирования в DefectDojo. Ее падение означало, что результаты по окончанию сканирования не отправлялись в DefectDojo. И в этом, как вы наверняка догадываетесь, уже есть пол беды.

Если обратить внимание на стейдж code-scan, в нем можно увидеть по названиям job, какие конкретно инструменты использовались для проведения сканирований. Да, теоретически разработчики могли бы обращать на эти джобы внимание, однако это мало кому было интересно. Более того, как уже писал ранее, мы не хотели негативно влиять на Time to Market — в том числе из-за этого мы сделали так, что все job, в которых выполнялись сканирования, принудительно завершались успехом. Судя по этим статусам разработчик мог сделать ложные выводы о том, что проблем нет вовсе ни со сканированием, ни с безопасностью. 

Если подытожить, на тот момент downstream-пайплайн был малоинформативен для разработчиков. Самый большой минус этой ситуации в том, разработчики, коих всегда в 10+ раз больше, чем AppSec-инженеров, не смогли бы помочь с триажом файндингов, даже если бы и захотели, из-за отсутствия инструментов и информации.

Что могло пойти не так?

А как тем временем дела у AppSec-ов, которые закольцевали на себя и DefectDojo весь процесс управления уязвимостями? И вот тут как раз самое время рассказать про боль, слёзы и достать из пыльного сундука умение признавать свои ошибки. 

В какой-то момент (прям практически «откуда ни возьмись») компания начала стремительно расти: увеличивалось количество команд разработки и микросервисов. Рост числа сервисов логично привел к росту числа сканирований, выполняемых на каждый коммит/MR, и увеличению количества файндингов в геометрической прогрессии. В какой-то момент, даже с включенной дедупликацией, в DefectDojo накопилось около 5 миллионов файндингов. Это, к сожалению, привело к деградации производительности и даже иногда к полному отказу в работе DefectDojo из-за утечек памяти.

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

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

Обобщим проблемы, возникшие в ходе попытки построить DevSecOps-процессы по «классическому» пути:  

  • Отсутствие обратной связи разработчикам по результатам сканирования. Разработчики, даже если бы хотели помочь AppSec-ам справиться с огромным числом файндингов, не смогли бы этого сделать, поскольку этого не позволяли выстроенные на тот момент процессы. 

  • Неэффективная система управления уязвимостями. DefectDojo, являющаяся ключевым иснтрументов для выстраивания процессов управления уязвимостями, не справлялась с нагрузкой, что привело к ситуации: «что она есть, что ее нет». 

  • Миллионы файндингов некому было обработать. Сильно ограниченное число AppSec-ов не позволяло справляться с таким объемом результатов сканирования. Не работавшая DefectDojo усугубляла ситуацию.

Пришло время изменений.

Выстраиваем принципы DevSecOps

Для того, чтобы повысить шансы успеха в ходе изменения DevSecOps-процессов, необходимо было придерживаться важных принципов, которые лежат в основе всех современных подходов к организации процессов безопасности.

О каких принципах речь:  

  1. О безопасности думают все, не только безопасники. При наличие отдела InfoSec многие разработчики, специалисты эксплуатации и управленцы самых разных звеньев, считают, что вся работа по безопасности и все риски лежат именно на нём. Но правда заключается в том, что риски ИБ ровным слоем распространяются на всех, кто так или иначе влияет на безопасность защищаемой системы/продукта. Поэтому о безопасности необходимо задумываться абсолютно всем участникам процесса. Если вас возмущает эта мысль, я поясню, что на самом деле делают специалисты по информационной безопасности — внедряют различные практики/методологию/инструменты, выполняют контроль за соблюдением требований безопасности и т.д.

  2. Self-service — автоматизируется все, что поддается автоматизации,  
    для увеличения зрелости внедряемых практик. Если не автоматизировать процессы, можно остановиться в развитии и закопаться в рутинных операциях. 

  3. Everything as code — ничего не делается заново и не допускается Configuration drift. Все политики ИБ и настройки инструментов сканирования выполняются только через SCM с применением стандартных практик как, например, ревью кода и прочих. Это также повышает прозрачность процессов ИБ, что позитивно сказывается на доверии к внедряемым практикам. 

  4. ИБ по максимуму переиспользует уже существующие инструменты. Если отдел информационной безопасности понимает, как адаптировать необходимые процессы к уже используемым в разработке инструментам (или наоборот), то он должен их использовать не прибегая к внедрению новых. 

Помимо принципов, есть еще и идея, на которой основано изменение в подходе к построению процесса DevSecOps — триажом на самом деле могут заниматься не только AppSec-и, но и разработчики

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

С триажом могут справиться разработчики уровня middle/middle+ и уж тем более security-чемпионы, если у вас таковые есть. Более того, разработчик в контексте собственного кода, что даст ему возможность быстрее принимать решение — найдена уязвимость или False Positive. 

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

DevSecOps: попытка №2

Первое, с чем следовало разобраться — с DefectDojo. Пересмотрев все его функции, мы выявили те, которые нам необходимы, и поняли, что можем полностью отказаться от этого инструмента, заменив его частично другими и реализовав некоторый функционал самостоятельно. Например, показывать результаты сканирования мы можем в GitLab job output. Плюс в том, что результаты станут доступны сразу и разработчикам, и AppSec-ам.

Для того, чтобы сделать результаты сканирования репрезентативными и интуитивно понятными в том числе и разработчикам, мы поработали над набором job в downstream-пайплайне, где выполняются все наши проверки безопасности, а также над выводом самих результатов. 

Что конкретно мы изменили в downstream-пайплайне:

  • сделали отдельные джобы с результатами сканирования для каждого типа сканирования: на скриншоте ниже видны 3 джобы с названием формата {тип_сканирования}: results, в которых и содержатся те самые результаты сканирования, которые можно анализировать;  

  • сделали статусы джобов с результатами сканирования информативными: зелёный (pass) означает, что файндингов нет, оранжевый (warning) — надо обратить внимание, есть файндинги, которые следует обработать;

  • обезличили и сгруппировали джобы со сканерами: как я уже отмечал выше, разработчикам, как правило, дела нет какие сканеры и какое их количество применяется для проведения анализа безопасности. Показывать стоит только результаты, все остальное — внутренняя кухня ИБ. 

  • убрали интеграцию с DefectDojo поскольку отказались от этого инструмента;

  • добавили job с QG (quality-gate): получив результаты сканирования, мы автоматически проверяем уровень риска ИБ относительно приемлемого уровня и выставляем оценку в виде статуса завершения job с QG. О QG будет более подробно далее.

635d94463ad131f901e7759daced063b.png

В job-ах с результатами по каждому из типов сканирования выведена вся необходимая информация для выполнения триажа:

3d7a3ef850bddcee3fa0da3b61c6417d.png

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

Окей, результаты мы отображаем в job output, но что с ними делать дальше? Далее следует дать возможность разработчикам, как и AppSec-ам, работать с результатами сканирования.

Какие существуют варианты обработки фандингов:  

  • если файндинг действительно является уязвимостью, исправить;

  • если речь про False Positive — добавить его в исключения.

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

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

Для добавления исключений был создан отдельный проект в GitLab, в котором разработчик может создать папку для своего проекта и добавить конфигурационный файл указав в нем необходимые исключения. Исключения могут создаваться для каждого типа проверок. Исключить файндинг можно по всем полям, которые видны в результатах сканирования — по идентификатору, директории, файлу, расширению файла, типу уязвимости/секрета. Помимо перечисленных есть отдельный тип исключений, общий, в котором можно использовать регулярные выражения для любого поля и указывать несколько паттернов для одного исключения, если необходима бОльшая гранулярность. Также реализована возможность добавления временных исключений. 

Процесс добавления исключения для разработчика выглядит следующим образом:  

  1. В определенном репозитории необходимо завести папку для своего сервиса.

  2. В папке создать конфигурационный файл с исключениями (пример на скриншоте ниже).

  3. Сделать MR

  4. После проверки со стороны AppSec и вливания в master, конфиг будет принят и учтен уже при следующем сканировании.

807de00a6fd04cfbb8d3c3ff8e6b5b7f.png

После того как мы реализовали инструмент, которым могут пользоваться в том числе и разработчики, следует поговорить о мотивации, без которой, конечно, все бы получилось, но медленнее.

Мотивация

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

Мы лишь наметили планы на внедрение QG в боевом режиме и конечно будем включать его постепенно. Но механизм уже отлажен и работает довольно просто. 

Quality Gate — это отдельная job, в которой собираются результаты всех типов сканирований и по каждому из них выставляется оценка — пройден/не пройден. Если QG не пройден хоть по одному из типов проверок, вся job фейлится (на текущий момент завершается со статусом warning). 

У каждого файндинга есть уровень критичности и, в соответствии с этим уровнем критичности, выставлен вес. Также для каждого типа проверки есть верхняя граница допустимого уровня риска (threshold). Соответственно, чтобы выставить оценку по каждому типу сканирования, мы складываем все веса всех файндингов и сравниваем с допустимым уровнем риска. Если сумма весов больше допустимого уровня риска, QG не будет пройден.

Пример результата работы QG:

ae93ed3ddc1b5f312c830bae25ab4cc5.png

Помимо внедрения QG, подсвечивать проблемы безопасности и доносить их до разработчиков помогает отдельный сервис Скоринга, реализованный для оценки качества сервиса посредством выставления оценок по различным метрикам. Эти метрики: процент покрытия автотестами, утилизация CPU и памяти, соблюдение SLO. 

Мы добавили в этот сервис метрики безопасности по результатам сканирования. Как это выглядит:

0c1d140fcaaf49f98e0d7483c273c354.png

В Скоринг, как видно из скриншота выше, выведены метрики отдельно по всем типам проверок, выполняемых для анализа безопасности исходного кода сервиса.

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

Что получилось в итоге

Итоговая схема сканирования на текущий момент выглядит следующим образом:

58eb58e4641e27565ff1312bd18aac01.png

  1. Основной пайплайн сборки и тестирования проекта (upstream) так же как и прежде запускает downstream с проверками безопасности с помощью trigger job. 

  2. В downstream проводятся все те же проверки безопасности.

  3. Получив результаты сканирования, перед тем, как их смерджить и показать, применяются исключения, если они уже добавлены для конкретного проекта в S3. 

  4. Метрики отправляются в сервис Скоринга. 

  5. Результаты сканирования демонстрируются в job output. 

  6. Если в результатах есть файндинги, разработчик самостоятельно их анализирует. Если найденный файндинг — ложное срабатываение, разработчик добавляет исключение в необходимый репозиторий и ждет аппрува от AppSec-ов. После аппрува исключения отправляются в S3 и будут применены для уже следующего сканирования.

Важно подчеркнуть, что в данной реализации основной пайплайн дожидается результатов дочернего. Это необходимо для корректной реализации QG.

Вместо заключения

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

Предлагаю вместе менять этот мир так, чтобы безопасности не требовалась отдельная буква в аббревиатуре SSDLC.

Если у вас есть вопросы, предложения или даже критика, пожалуйста, не стесняйтесь — буду рад ответить в комментариях. 

Tech-команда СберМаркета ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на YouTube. А также слушай подкаст «Для tech и этих» от наших it-менеджеров.

© Habrahabr.ru