Привет, Хабр! С вами Максим Коровенков, DevSecOps Lead в Купере. Продолжаем цикл статей про построение DevSecOps с нуля. Это большой гайд from zero to, надеюсь, hero.
Наш контекст подразумевает девелопер-центричную модель, но убежден, что инструменты и процессы, о которых я рассказываю, применимы в любых других условиях реализации процессов безопасной разработки.

Вы можете начать с этой статьи. Но если хотите по-настоящему углубиться, рекомендую ознакомиться с предыдущими:
Через тернии к звездам: строим SSDLC на OpenSource-компонентах
Вы таки внедрили сканеры безопасности в пайплайны — на этом все?
Мы построили процесс управления файндингами от сканеров по девелопер-центричной модели. Результаты получают как разработчики, так и AppSec-специалисты. При этом вторые подключаются только в том случае, если нужно согласовать фиксы или пометки разработчиков о False Positive, либо если разработчикам нужна помощь в обработке файндинга. Например, в понимании подхода к исправлению той или иной уязвимости.
Девелопер-центричность нужна нам для снижения нагрузки на AppSec-специалистов путем передачи части работы по FPA на разработчиков. Нам это кажется логичным. Разработчики должны писать качественные сервисы, качество подразумевает безопасность. Мы предоставляем разработчикам безопасность, а именно сканеры и всю платформу, позволяющую обрабатывать файндинги, as a service. Разработчики могут практически самостоятельно обрабатывать найденные потенциальные проблемы безопасности.
Но это только начало нашего большого пути. Мы внедрили сканеры и научились работать с результатами сканирований. Что насчет построения более зрелых процессов и поиска ответов на более сложные вопросы AppSec-специалистов?
Вот только часть волнующих тем:
Как понять, что та или иная уязвимость нашлась в нескольких сервисах? Это необходимо, чтобы либо добавить исключение сразу для всех сервисов, либо сделать выводы о том, что определенные проблемы носят массовый характер и необходима приоритезация.
Как запускать сканирования в моменте? Мы добавили сканер, изменили его конфигурацию или реализовали кастомную проверку. Чтобы обновить общие метрики по всем сервисам, придется ждать, когда пайплайны отработают для всех сервисов. Это долго. Почему бы не научиться запускать сканирования по расписанию или по требованию?
Что, если мы хотим хранить информацию о последнем проведенном аудите или о степени критичности сервиса и как-то использовать архив в своих автоматизациях? Где вести записи и как обновлять?
Чтобы вывести AppSec-процессы на новый уровень, хорошо бы внедрить такие решения:
Единая база данных для AppSec-специалистов. Она должна сочетать в себе данные, источниками которых являются сами AppSec-специалисты, и взятые из нескольких источников данные о сервисах.
Сервис управления уязвимостями, найденными как в пайплайнах, так и на любом другом этапе разработки или вне ее. Сходу на ум приходит ASOC, но об этом дальше.
Сервис управления сканированиями для анализа сервисов по требованию или расписанию.
Выбор ASOC
ASOC и решения подобного класса помогают полноценно управлять уязвимостями — это неоспоримый факт. Но их нельзя покупать вслепую. Сначала нужно тщательно проанализировать процессы вашей компании и сформировать список требований.
Я вижу несколько функциональных возможностей, на которые принципиально важно обращать внимание:
Организовывать знания о структуре проектов и сохранять все необходимые метаданные сервисов
Демонстрировать подробную информацию об уязвимости
Выводить топ по уязвимостям
Указывать сервисы, где найдена та или иная уязвимость
Работать с уязвимостями: изменять статусы или любую другую информацию
Тут придется сделать длительную остановку. Как я уже сказал, мы внедрили девелопер-центричную платформу, DSOP, и с ней могут работать как разработчики, как и AppSec-специалисты.
Возможности, которые предоставляет платформа для обработки файндинга: исключить его либо временно, либо навсегда; либо для одного сервиса, либо для всех сразу.
Когда файндинг мог быть исключен?
Если он False Positive
Если риск принимался
Если уязвимость подтверждалась, но ее исключали временно — чтобы пройти проверку QG, не останавливая релиз
Одно действие (исключение) может иметь разную смысловую нагрузку.
А мы хотим добавить ASOC, в рамках которого тоже можно обрабатывать файндинги. Очевидно, статусы файндингов в ASOC и DSOP необходимо будет синхронизировать. Об этом в следующем блоке, а пока добавлю несколько функциональных требований к ASOC, исходя из необходимости синхронизации:
Отправлять вебхуки по определенным событиям
Иметь API с возможностью автоматизированного управления файндингами
Так как помимо задачи управления уязвимостями мы также решаем задачу управления сканированиями, есть еще одно требование:
Возможность управления сканированиями из ASOC
Благо мы знаем, что большинство решений класса ASOC на это способны.
Мы, собрав список минимальных требований, весело отправились на рынок. Нам казалось, что мы идем по грибы в лес после теплого дождя, но… леса не оказалось. Скорее, мы наткнулись на полянку с двумя поганками. Ни одно решение не могло закрыть все наши потребности. А покупка бесполезного инструмента не входила в наши планы.
Но выход нашелся! О нем рассказал мой коллега Семен Барышников на DevSecOps-митапе Купер.теха. Мы договорились с одним из вендоров, что приобретем ASOC, если они за определенный срок реализуют все, чего нам не хватает. И это сработало! Через квартал мы начали внедрять обновку.
Синхронизация ASOC и DSOP
Идем к одной из самых сложных задач, что мы решали.
ASOC нам был нужен, чтобы иметь больше информации об уязвимостях. Но еще мы получили возможность прямо в нем обрабатывать файндинги, которые до этого обрабатывали только в девелопер-центричной манере — с помощью коммитов-исключений в DSOP.
Странно было бы запретить AppSec-специалистам управлять уязвимостями в ASOC. Одно действие в UI — гораздо проще коммита в даже в самом простом YAML-конфиге.
Но и лишиться девелопер-центричности мы не могли: разработчикам должна быть доступна обработка файндингов прямо в GitLab, как это было изначально, без необходимости использовать внешнюю информационную систему (в нашем случае ASOС).
Соответственно, события в ASOC и DSOP должны быть синхронизированы.
Внутри DSOP, как я писал выше, можно только исключить или временно исключить файндинги. Каждое из этих действий может иметь несколько смыслов сразу. В ASOC же существует гораздо большее количество статусов для обработки файндинга: подтвердить, решить, отклонить, принять риск временно/постоянно.
Для синхронизации статусов мы реализовали специальный сервис, который обрабатывает отчеты из DSOP и события из ASOC и по определенным правилам синхронизирует статусы файндингов.
Если статус файндинга изменился в DSOP (разработчик добавил исключение), сервис меняет статус в ASOС. Если AppSec меняет статус в ASOC, сервис добавляет исключение в репозиторий с исключениями. Почти так же, как если бы это сделал разработчик.
Разберем подробнее логику изменений:
Если файндинг False Positive, в DSOP заводится постоянное исключение; в ASOC файндинг переводится в «Отклоненные».
Если файндинг True Positive и нужно исправление, в DSOP заводится временное исключение на период, в зависимости от уровня критичности уязвимости; в ASOC файндинг переводится в статус «Временно принятый риск» на тот же срок. Не в «Подтвержденные» — это сделано в угоду более простому соотнесению статусов между двумя системами.
Если файндинг True Positive, но исправлять не нужно, в DSOP заводится постоянное исключение; в ASOC файндинг переводится в «Постоянно принятый риск».
Да, не все очевидно. Местами требуются прописанные регламенты. Но задача синхронизации решена, девелопер-центричность сохранена. При этом AppSec-специалисты могут работать с файндингами в ASOC.
Что ценно — сервис синхронизации умеет в массовые действия. Например, AppSec фильтрует файндинги в ASOC, выбирает сразу несколько и переводит их в статус «Отклоненные». Сервис коммитит исключения в конфигурационные файлы для каждого сервиса, где был найден файндинг.
Инвентаризация в ASOС
Чаще всего результаты сканирования отправляются в ASOC из пайплайнов POST-запросом. Возможно, с некоторым простым условием: «Если такой сервис есть, дай ID, сохрани для него файндинги. Если нет, заведи новый с названием из какой-нибудь переменной окружения. Снова дай ID и сохрани для него файндинги».
Что плохого в таком подходе? Через время вместо актуальных данных о сервисах вы получите хаотичную свалку. Вы не сможете снимать точные метрики, даже банальное количество файндингов в сервисах, потому что не будете уверены в актуальности информации.
Чтобы ASOC реально был полезен AppSec-специалистам, необходимо добавлять туда данные о сервисах из разных источников — и актуализировать их.
Вдобавок вспоминаем о единой базе данных. ASOC мог бы быть одним из основных ее потребителей.
Так мы пришли к реализации собственного сервиса, который назвали AppSec DataStore (ASDS).
Чего мы хотели от ASDS?
Консистентность данных для всех инструментов/автоматизаций, которые затрагивают процессы AppSec
Отсутствие необходимости реализовывать отдельный сбор данных для каждой новой автоматизации. Если необходимых данных нет в ASDS, мы думаем над тем, как добавить их именно туда
Возможность собрать полный архив данных и обогащать их своими данными для принятия более взвешенных решений о рисках
Какие источники данных мы решили использовать для ASDS?
GitLab
Наш PaaS
Данные из пайплайнов
CMDB (база данных управления конфигурациями, в нашем случае — Jira Insight)
Внутренние конфигурационные файлы, актуализирующиеся AppSec-специалистами
Примеры данных, которые мы собираем с помощью ASDS для сервисов:
Актуальность проекта в GitLab
Актуальность сервиса в CMDB
Наличие актуальных уязвимостей
Информация о команде
Информация об AppSec BP
Mattermost-канал команды
Критичность сервиса
Примененные CI/CD-шаблоны для проекта (чтобы понимать, какие сканеры должны отрабатывать в пайплайнах)
Языковой стек проекта
Статус QG (включена или нет блокировка)
Время последнего пайплайна с проверками безопасности
У ASDS есть свои файлы конфигурации — например, со списком родительских неймспейсов проектов, которые являются неактуальными или просто не должны отслеживаться AppSec-специалистами.
Неактуальные проекты делятся на два типа: те, которые должны быть полностью удалены из ASDS/ASOC, и те, которые остаются, но не подлежат учету в метриках.
Данные в ASDS актуализируются с помощью набора асинхронных задачек, они отрабатывают раз в N часов. После, с помощью таких же задачек, по данным из ASDS синхронизируются данные в ASOC.
Все дополнительные данные о сервисах реализованы в ASOC в виде тегов, которые могут использоваться для фильтрации.
В итоге мы получаем всю актуальную информацию для AppSec-специалистов в ASOC и можем гранулярно фильтровать сервисы и уязвимости.
Как выглядит информация по сервису в ASOC:

Казалось бы, зачем заморачиваться и делать отдельный сервис для актуализации данных в ASOC? Среди потребителей ASDS — не только ASOC, но и AppSec-специалисты, которые выполняют аналитику, а также написанные ими сервисы автоматизации.
Все данные, представленные в ASDS, сохраняются в виде Prometheus-метрик и отображаются на дашбордах в Grafana.
Уязвимости из пайплайнов сохраняются в S3, далее ASDS их обрабатывает и, синхронизируя со статусами в DSOP, сохраняет в ASOC. Такая схема импорта отчетов в ASOC в том числе повышает отказоустойчивость.
Почему S3, а не RabbitMQ или Kafka? S3 — самый простой и по многим критериям лучший способ создания инфраструктуры очередей в тех случаях, когда важна сохранность данных и не критичен порядок сообщений. Учитывая, что мы почти везде используем S3 в роли объектного хранилища данных, выбор был предрешен.
Сервис сканирования
Напомню, мы хотели не только управлять данными о сервисах и уязвимостях, но и сканировать любой набор сервисов компании в моменте — любым набором сканеров. Ранее все сканы были частью пайплайнов и запускались вместе с основными конвейерами сборки. Так что мы создали сервис с крайне оригинальным названием Scanner Service =)
Посмотрим, что он умеет и как мы выстроили процессы.
Обновление метрик
До внедрения ASOC единственной информацией, которой мы располагали по результатам сканирования, были метрики скоринга, показывающие наличие/отсутствие уязвимостей разного уровня критичности в виде оценки от A+ до C. Еще мы реализовали бот, который ежедневно сообщал об ухудшении оценок сервисов.
Все отлично работало, но не в моменты, когда мы хотели изменить конфигурацию сканера или добавить новый сканер. Мы не могли понять, какие оценки изменились/ухудшились, пока во всех сервисах не прошли пайплайны со сканерами.
Решить проблему помог союз Scanner Service и ASDS.
На первом этапе мы реализовали пайплайн scan-pipelines со всеми теми же джобами/проверками, которые запускаются при выполнении пайплайнов сервисов. Вместе с ними реализовано еще две джобы: скачивание кода нужного репозитория и очистка кэша. Пайплайн принимает на вход несколько переменных и на их базе понимает, какой репозиторий сканировать и какие проверки выполнять.
Логика:
Скачиваем код сервиса и сохраняем в виде архива в S3
Генерируем 15-минутную ссылку для скачивания архива из S3 обычным curl
В рамках каждой джобы, где выполняется сканирование, скачиваем и распаковываем архив с репозиторием
Проводим сканирование
После всех джобов удаляем архив из S3
На втором этапе мы сделали так, чтобы Scanner Service каждый день по расписанию выгружал список всех актуальных сервисов/репозиториев и триггерил пайплайн сканирования из первого этапа.
Scanner Service просто управляет пайплайном scan-pipelines, запуская его с определенными переменными и отслеживая статус. Что происходит внутри — Scanner Service не знает.
А где метрики обновляются? Внутри пайплайна, кроме джоб, выполняющих сканирования, есть также те, которые входят в DSOP. В них обрабатывается сканирование, демонстрируются результаты по типам проверок (в stdout) и обновляются метрики.
Запуск сканирования любым сканером
Выбранный нами ASOС умел управлять сканированиями «из коробки», но нас не устроила реализация. Поэтому мы организовали весь функционал через свой Scanner Service. Конечно, для этого мы предварительно подебажили API-методы ASOC. Возможность запускать сканирования из ASOC оставили, но все, что касается процесса сканирования, реализовали сами.
В двух шагах — как добавить любой сканер в ASOC и проводить с его помощью сканирования:
Сделайте парсер для отчета сканера. Например, в нашем ASOC можно добавлять кастомные импортеры. Если у вас нельзя — любая стандартная форма загрузки результатов в помощь. Но предварительно нормализуйте отчет на своей стороне.
Реализуйте джобу/проверку в том же проекте scan-pipelines. Все, что нужно указать: докер-образ со сканером, команда сканирования (в секции script) и в переменных добавить название парсера, который заранее подготовлен в самом ASOC.
Пример джобы для кастомного сканера:

Все необходимое есть в шаблоне джобы custom_scanner_config: скачивание кода, отправка отчета в ASOC, интеграция с Vault и т. д.
Когда происходит пуш с новой джобой для нового сканера, отрабатывает CI проекта scan-pipelines, где список всех джобов с кастомными сканерами передается в Scanner Service.
Scanner Service в свою очередь выявляет новую джобу с новым сканером и создает сканер внутри ASOC. И вуаля! Теперь AppSec-специалисты могут запускать сканирования для любого набора сервисов с помощью Scanner Service и нового сканера в ASOC.
Под капотом все работает так же, как в пункте выше. При запуске сканирования в ASOC запрос передается в Scanner Service. Scanner Service триггерит scan-pipelines и передает необходимый набор переменных. В том числе имена сканеров, которые использовали AppSec-специалисты при старте.
Названия сканеров в ASOC соответствуют названиям джоб в scan-pipelines. При триггере запускается набор джоб, соответствующий тому набору сканеров, который использовали AppSec-специалисты.
Тестирование кастомных правил Semgrep
Когда-то я реализовал функционал, чтобы добавлять кастомные правила сканирования в Semgrep. Было несложно — у Semgrep встроены функции линтинга и тестирования правил.
Какая логика? AppSec-специалисты кладут правило в репозиторий devsecops-configs, CI отрабатывает и тестирует правило и, если все нормально, пересобирает докер-образ с Semgrep. Образ с новым правилом используется в пайплайнах сервисов сразу после пересборки.
Довольно быстро появился вопрос о внедрении возможности тестирования правил в продовых сервисах, но не в пайплайнах.
С одной стороны — чтобы было проще проверять, отрабатывает ли правило и во всех ли случаях. С другой — чтобы не добавить правило, которое потенциально может остановить релиз, если в сервисах внедрен QG в блокирующем режиме.
В CI проекта devsecops-configs, куда AppSec-специалисты складывают кастомные правила для Semgrep, были добавлены джобы для отправки потенциальных новых правил в base64 в Scanner Service.
Scanner Service сохраняет правило и создает сканер с названием semgrep_{название правила} в ASOC. И далее AppSec-специалисты могут запускать сканирования с помощью Semgrep с новым правилом через ASOC для любого набора сервисов.
В этом случае Scanner Service не запускает пайплайны, а работает как GitLab K8s executor — запускает для сканирования каждого сервиса K8s под с Semgrep. Как только сканирование выполнено, отчет отправляется в ASOC (вот и найден обычный POST!), после чего K8s под убивается.
Все уведомления о старте и финале сканирований c помощью Scanner Service приходят в виде пушей в Mattermost-канал.
AppSec анализирует файндинги в ASOC и, убедившись, что правило работает, деплоит его в master в проекте devsecops-configs. Здесь же он может запустить джобу, которая удалит кастомное правило с помощью Scanner Service, чтобы оно не висело в ASOC.
И это только несколько процессов, где задействован наш Scanner Service =)
Собираем все вместе
Возможно, у дорогого читателя сейчас в голове нечто подобное:

Чтобы упорядочить хаос, дарю вам схему, которая объясняет, как эти три инструмента уживаются вместе.

Коротко — за что отвечает каждый сервис:
В DSOP происходит следующее:
— Сканирование безопасности в пайплайнах
— Показ результатов сканирования в GitLab Job Output
— Обработка результатов разработчиками и AppSec-специалистами
— Отправка метрик в Prometheus
— Отправка результатов сканирования в S3
ASOC позволяет управлять уязвимостями и сканированиями
ASDS синхронизирует результаты сканирования ASOC и DSOP и актуализирует информацию о сервисах
Scanner Service регулярно проводит сканирования всех сервисов для обновления метрик, а также сканирования, инициированные ASOC
Переводим «хотелки» в лучшие практики
Я несколько раз упоминал «хотелки» наших AppSec-специалистов. Но почему это должно быть интересно кому-то, кроме энтузиастов в области нестандартных подходов к DevSecOps? Ведь это наш личный опыт, и он никак не коррелирует с лучшими практиками по ИБ, AppSec и DevSecOps…
Или все-таки коррелирует?
Начнем с ГОСТ 56939-2024. В нем есть требование об организации процесса реагирования на уязвимости (пункт 5.23). За общими словами о «сведениях об анализе ошибок» кроется суть. Речь идет о построении процесса для управления уязвимостями, и его логичной частью будет система класса ASOC.
Более наглядный пример — российская методология AppSec Table Top. Ее большое преимущество в том, что она отвечает не только на вопрос «Что делать?», но и на вопросы «Как?» и «С помощью каких инструментов?». Я ничего не рекламирую, но советую обратить внимание, если вы хотите укрепить защиту своих веб-приложений.
Практики и инструменты, которые я дал в этой статье, легко соотносятся с дорожной картой AppSec Table Top:
PreStageDL, порядок контроля используемого ПО (TP2). Мы частично выполняем этот пункт с помощью ASDS, когда инвентаризируем свои сервисы и определяем их критичность
MainStageSDL, оркестрация SAST/SCA/DAST (SPA6, SCA4, DPA5). Реализовали с помощью Scanner Service!
PostStageSDL, управление уязвимостями (все VM). Подразумевается решение класса ASOC
Теперь справедливо сказать, что Купер.тех внедряет лучшие практики по организации продуктовой безопасности! С учетом опыта, контекста инфраструктуры и процессов компании.
Пара слов вдогонку
За год мы сделали, не побоюсь этого слова, прорыв в безопасности приложений. ASOC, ASDS и Scanner Service значительно увеличили потенциал всей AppSec-команды. Теперь мы с коллегами можем гораздо эффективнее решать рутинные задачи и быстрее и качественнее управлять рисками.
Буду рад, если вы поделитесь размышлениями и опытом в комментариях. Давайте вместе делать DevSecOps лучше!
Tech-команда Купера ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на YouTube.