Защита репозиториев кода: правила веток, обязательные ревью и хранение секретов

Изображение: recraft
Исходный код приложения или его конфигурационные файлы являются одними из самых ценных активов. Пренебрежение правилами их защиты в процессе разработки и хранения в репозитории может привести к критическим финансовым и репутационным потерям. В данной статье инженер-разработчик компании STEP LOGIC Павел Пилькевич расскажет о трёх китах защиты репозиториев: управлении ветками, процессе код-ревью и обращении с секретами.
В течение нескольких лет наша команда разрабатывает платформу STEP Security Data Lake (SDL), предоставляющую набор инструментов для анализа больших массивов машинных данных для наблюдения за состоянием защищённости сети и управления инцидентами ИБ. Изначально, изучая опыт других разработчиков, мы пришли к выводу, что хорошо защищённый репозиторий кода – фундамент безопасности всего цикла разработки ПО, поэтому наша система защиты хранилища базируется на жёстких правилах создания веток, грамотном код-ревью и надежном хранении секретов.
Расскажу подробнее о каждом пункте.
- Правила веток позволяют разграничивать доступ в возможностях слияния изменений, ограничивают фиксацию изменений напрямую в ветке.
- Код-ревью подразумевает проверку написанного кода старшим разработчиком с целью обнаружения в нём формальных несоответствий, уязвимостей, опечаток, а также любых других недостатков, которые негативно влияют на ПО.
- Грамотное хранение секретов – основа распределения доступа в соответствии с ролями пользователей в приложении и самом цикле разработки.
На основе накопленных знаний и опыта мы выработали пошаговый алгоритм, который помогает обеспечить безопасность репозиториев кода.
Шаг 1: Зафиксируйте правила создания и наименования веток
Ветки создаются исключительно на основании задач/карточек. Наименование ветки должно быть интерпретируемым и включать в себя ссылку на номер карты. Это позволяет избежать хаоса в репозитории. Например, название ветки «10-fix-check_user_exceptions» или «12-docs-add_params_description» значительно легче читаются, в отличие от «fix_func» или «clear_leg_v1». Указывайте в названии ветки, к какой группе задач она относится, а также полноценное краткое описание.

Шаг 2. Ограничьте слияния с главными ветками
Любые изменения должны быть приняты старшим разработчиком. В главную ветку изменения могут попадать исключительно через запросы на слияние (Merge Request) с необходимостью подтверждения этого запроса старшим разработчиком (Approve). Так получается обезопасить главную ветку от возможных опечаток или других мелких ошибок разработчиков. Например, в ходе решения большой задачи разработчик мог забыть удалить куски кода, которые использовались им для отладки приложения. Свежий взгляд на код со стороны другого человека позволит обнаружить такие проблемы. В связи с этим также стоит помнить о размерах вносимых изменений в рамках одного запроса на слияние: не делайте их слишком большими по размеру, так как такие изменения, как правило, не подвергаются тщательной проверке, что повышает риск попадания уязвимостей в исходный код основного приложения.
Шаг 3. Исключите возможность переписывания истории репозитория
Полный запрет на переписывание истории репозитория (Force Push). Переписывание истории ведёт к непониманию полного процесса разработки, когда им занимается несколько человек. В отдельных случаях это приводит к невозможности отката конкретных изменений для выполнения быстрых правок (hotfix).
Шаг 4. Внедрите культуру ИБ в код-ревью
В рамках данного шага хочу акцентировать своё внимание на том, что код-ревью должно затрагивать стратегию фиксации изменений, наименования веток, способность разработчика ориентироваться в управлении репозиторием стандартными средствами (git). Например, разработчик может коммитить слишком большие куски кода сразу, что приводит к усложнению ревью и невозможности отменить конкретные элементы средствами встроенной команды git revert. В данном случае будет полезно напомнить о необходимости коммитить «пошагово», а не всё сразу, с объяснением причин данного требования.

Шаг 5. Не храните секреты в открытом виде
Хранение секретов – чрезвычайно важная тема для любых репозиториев. Например, в нашем решении STEP SDL все конфигурационные файлы хранятся в удалённом репозитории, фактические изменения конфигурации также фиксируются в нём. При этом критически важно отслеживать, чтобы ни один из секретов не попал в репозиторий в открытом виде.
Что делать в таких случаях? Вот основные правила:
- Рассудите, действительно ли вам необходим тот или иной файл внутри удалённого репозитория. Если есть возможность представить его в формате незаполненного шаблона – зафиксируйте его у себя в репозитории, а результирующее имя файла поместите в перечень игнорируемых данным репозиторием файлов .gitignore. Например, есть конфигурационный файл, который вы принудительно игнорируете через .gitignore, а его незаполненную версию с комментариями кладёте в репозиторий с дополнительным расширением .template.
- Всегда уходите от использования паролей напрямую. Где это возможно – используйте безопасные хранилища, а внутри файлов оставляйте лишь ссылки на них (HashiCorp и другие). Если нет возможности использовать безопасные хранилища – постарайтесь переместить секреты в переменные окружения.
- Мы рекомендуем внедрять механизмы сканирования файлов исходного кода на наличие в них оставленных секретов. Наиболее эффективным методом в данном случае является добавление git hook для проверки кода перед его фиксацией в репозитории.
Защита репозитория – это не разовое действие, а комплекс процессов, внедренных в культуру разработки, с которой должен быть знаком каждый участник команды. Правила для веток, обязательные ревью и безопасное хранение секретов создают «защитный пояс», который предотвращает множество проблем до их попадания в продакшен, а многие из них пресекаются даже до попадания в историю репозитория.
