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

Ко мне это понимание пришло в день, когда после исследования нового проекта на уязвимости (статический и динамический анализ) мы, как обычно, собрали замечания и предупреждения, создали задачи и отправили уведомления разработчикам. Однако было одно важное отличие: замечаний тогда набралось 250 штук. Поэтому разработчики, мягко скажем, удивились, обнаружив утром в таск-трекере 250 новых уведомлений. В результате вместо привычных «дада, все исправим» последовала обстоятельная встреча нашей DevSecOps-команды с командой разработки. Стало ясно, что в жизни надо что-то менять, и мы решили поменять взаимодействие с разработкой.

Меня зовут Натали Дуботолкова, и в «Базисе» я руковожу группой по обеспечению безопасности приложений. В прошлой статье я рассказывала о том, какие подходы и инструменты мы используем для реализации DevSecOps. Сегодня же хочу поделиться своим опытом выстраивания организационных процессов безопасной разработки.

Вздыхаем и учимся говорить на одном языке

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

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

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

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

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

Если все упростить и формализовать, то сценарий встречи-знакомства с командой разработки у нас сейчас выглядит следующий образом:

  • рассказываем о себе, рассказываем про РБПО, объясняем на примерах конкретные риски и их последствия;

  • договариваемся, в каком формате будем передавать информацию о найденных уязвимостях;

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

  • обсуждаем, что делать и кого «пинать», если задача просрочена;

  • выясняем тонкости работы, архитектуру, стеки команды разработки и, если возможно, адаптируем наши процессы с учетом этих вводных.

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

Как мы дошли до жизни такой?

Тут хочется сделать отступление и рассказать чуть больше о своем (и команды) опыте в «Базисе». В конце концов, наша работа началась не в «день 250-ти уведомлений», а значительно раньше. Почти три года назад нам было поручено внедрить статический, композиционный и динамический анализы в процесс сборки разных продуктов компании. Да, все решения «Базиса» так или иначе связаны с виртуализацией и контейнеризацией, но это все еще разные по своей сути решения. Более того, у каждой команды были собственные представления о процессах безопасной разработки и о том, насколько эти процессы нужны. Работа предстояла большая, а сложности начались практически сразу.

Первое, у каждой команды были свои процессы и своя культура. Одни собирают продукты в docker-контейнерах, которые сразу становятся дистрибутивами. Другие создают контейнер только в процессе сборки, забирая из него готовый артефакт. А третьи вообще не используют контейнеры, работая напрямую в нодах Jenkins. Менять процессы ради нас и безопасной разработки никто не собирался, мы же решили не навязывать свое видение и не пытаться унифицировать процессы. Это могло испортить отношения и обойтись слишком дорого с точки зрения ресурсов.

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

Третье, команды не совпадали друг с другом не только на уровне инструментов, но и на уровне архитектурного мышления. Кто-то делал ставку на скорость релизов и легкость экспериментов, создав гибкую, но хрупкую архитектуру. Кто-то, наоборот, выбрал стабильность, в результате процессы обросли слоями проверок и ручных согласований. Где-то десятки микросервисов, каждый со своей отдельной сборкой, где-то — монолит с собственным CI/CD-пайплайном.

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

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

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

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

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

Задача команды фаззинга свелась к постоянному анализу процессов — смотрим, что работает хорошо, а что нуждается в улучшении. Как в случае со статическими  анализаторами, где мы прошли путь от простого предоставления доступа разработчикам до создания централизованного docker-образа и полноценной интеграции в процесс сборки.

Сертифицируй это

Взаимодействие с разработкой вышло на новый уровень, когда у нас появились задачи, связанные с сертификацией продуктов. Нам пришлось глубже погружаться в модели угроз, выявлять поверхности атаки — это мог быть модуль, а мог быть и весь репозиторий или какая-то конкретная библиотека. Всю эту информацию надо было, не расплескав, донести до команд разработки. Опять же, не в формате «мы тут вам создали задачи, займитесь срочно», а встречаемся и объясняем, что нужно и зачем. И желательно приходить не с абстрактными идеями, а с протестированными вариантами решений, которые можно обсудить.

Помимо взаимодействия с разработкой и выстраивания процессов РБПО нужно было усиливать команду, нанимать DevSecOps-инженеров и специалистов по AppSec, закупать дополнительное оборудование. Формула «сертификация = РБПО = расходы» вырисовывалась все четче. Мы решили пойти по пути разумного роста, точечно усилив ключевые направления. В частности, наняли еще специалистов по фаззингу и купили серверы — я упоминала о них выше.

В целом про сертификацию и связанные с ней сложности один из моих коллег уже рассказывал на «Хабре», поэтому останавливаться на этом не буду.

Главное, что нам удалось сделать, — это изменить отношение разработчиков к процессам безопасной разработки. Тут нам во многом помогли громкие кибератаки и утечки данных последних лет, а также внимание к ним со стороны СМИ. Как минимум, дали наглядные иллюстрации и для многих сняли вопрос «зачем это нужно?». Мы же добавили взаимопонимания между командами и создали процессы, не требующие сотен человеко-часов от разработки.

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

И про автоматизацию не забываем

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

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

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

Security champion: когда автоматизации недостаточно

Однако как бы сильно и много мы ни автоматизировали, без роли security champion в командах не обойтись, и я очень рада, что наши ко��анды тоже это понимают. Крайне важно иметь такого человека, который «дышит одним воздухом» с разработчиками. Он живет внутри спринтов, знает все мельчайшие изменения в архитектуре еще до того, как они попадут в репозиторий, и своевременно может задать вопрос: «А мы не забыли про безопасность?» Это не контролер извне, а свой же коллега, который мыслит чуть более параноидально и помогает оценивать риски на самом раннем этапе.

Security champion является переводчиком с технического на безопасный и обратно. Он помогает фильтровать «шум» от сканеров, разработчикам на их же языке объясняет суть проблемы, а нам — архитектурный контекст, без которого наши рекомендации могли быть бесполезны. Я очень надеюсь, что данная практика у нас закрепится, и в каждой команде будет свой человек на эту роль. Тогда совместная работа станет еще спокойнее и эффективнее.

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

Поэтому глобально мы продолжаем улучшать процессы безопасной разработки, ориентируясь на требования регулятора и новый ГОСТ 56939-2024. В перспективе хотим пройти сертификацию нашей разработки безопасного ПО. Стараемся больше взаимодействовать с отраслевыми экспертами, перенимать их опыт. В частности,  вместе с ИСП РАН и ведущими вузами исследуем open-source-компоненты и находим дефекты в коде. С разработчиками отечественных инструментов для статического и динамического анализа тоже налаживаем контакты, есть примеры отличного взаимодействия с CodeScoring. И всегда готовы поделиться своим опытом, например, на мероприятиях, таких как «ТБ Форум», или в виде статьи, которую вы сейчас прочитали.