Внедряем DevSecOps в процесс разработки. Часть 2. Обзор инструментов, Commit-time Checks

от автора

Привет! На связи Олег Казаков из Spectr

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

В предыдущей части статьи я рассказал о том, что представляет собой процесс DevSecOps в целом, из каких этапов он состоит, и подробно остановился на первом этапе — Pre-commit Checks. Сегодня пришло время для обзора стадии Commit-time Checks и ее инструментов. Поговорим о каждом инструменте отдельно и расскажем, на чем мы все-таки остановили свой выбор.

Commit-time Checks

Суть этапа: проверить код на предмет корректности и безопасности в GIT-репозитории.

Рассмотрим известные классы инструментов.

SAST

SAST (static application security testing) — это процесс тестирования приложения на наличие ошибок и уязвимостей в исходном коде.

По этой ссылке доступен список различных видов SAST. Как видите, их довольно много, но практически у каждого вида есть сложности в использовании. Чаще всего это ложное срабатывание.

Этапы работы SAST-инструментов

Конечно, есть нюансы и различия, но в общем случае этапы следующие:

  • Построение модели (Modeled Code). На этом этапе инструмент SAST использует исходный код и преобразует его в формат, полезный для выполнения анализа. Одни инструменты компилируют код, другие используют абстрактное синтаксическое дерево для построения модели, третьи преобразовывают их в произвольный формат по своему выбору. Наиболее популярный формат — абстрактное синтаксическое дерево. Большинство инструментов SAST поддерживают несколько языков программирования, и этот шаг необходим для того, чтобы преобразовать код на любом языке в единый формат.

  • Поиск дефектов (List of Defects). На этом этапе инструменты SAST применяют различные правила к смоделированному коду. Эти правила могут быть определены поставщиком инструмента или написаны пользователем инструмента. Происходит семантический, структурный и прочие анализы, и на выходе мы получаем список дефектов

Виды SAST-инструментов

Среди возможных видов SAST есть платные и бесплатные инструменты, они перечислены ниже: 

Платные

Бесплатные

LGTM 

Checkmarx SAST
Contrast 
Coverity Scan
SonarQube
Semgrep
PVS-Studio
Reshift
Mend SAST
HCL AppScan Source

Open Source, которые в основном направлены на конкретные языки:
Bandit
Brakeman
Phpcs-security-audit
Gosec
Security Code Scan
Flawfinder
ESLint security plugin


Free Community Edition

Contrast Scan
SonarQube
Semgrep

SAST в GitLab

Посмотрим, что нам предлагает GitLab. У GitLab есть SAST во всех версиях.

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

GitLab предлагает возможность использования версий разных Open Source или Free Community Edition. Их можно включить простым кодом, представленным ниже:

include:   - template: Security/SAST.gitlab-ci.yml  sast:   tags:     - docker

То есть мы просто включаем SAST, добавляем файлики, например PHP-файл и Go-файл, таким образом добавляются стадии проверки при помощи phpcs-security-audit, Semgrep и Gosec (последние 2 на Go):

Ниже представлен результат сканирования phpcs-security-audit:

Можем посмотреть исходники — что здесь есть? 

Ниже приведен пример того, как подключенный шаблон этой задачи (Security/Secret-SAST.gitlab-ci.yml) выглядит в исходниках:

sast:   stage: test   artifacts:     reports:       sast: gl-sast-report.json   rules:     - when: never   variables:     SEARCH_MAX_DEPTH: 4   script:job     - echo "$CI_JOB_NAME is used for configuration only, and its script should not be executed"     - exit 1  .sast-analyzer:   extends: sast   allow_failure: true   # `rules` must be overridden explicitly by each child job   # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444   script:     - /analyzer run

В самом начале все по аналогии с тем, что мы видели в Secret Detection в рамках предыдущей статьи: есть сама задача SAST, генерация отчета (только уже с другим именем — gl-sast-report.json). Но отличия все же есть, т. к. GitLab под разные ЯП предлагает различные инструменты SAST, то ест для каждого из этих инструментов есть свое описание.

semgrep-sast:   extends: .sast-analyzer   image:     name: "$SAST_ANALYZER_IMAGE"   variables:     SEARCH_MAX_DEPTH: 20     SAST_ANALYZER_IMAGE_TAG: 4     SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/semgrep:$SAST_ANALYZER_IMAGE_TAG$SAST_IMAGE_SUFFIX"   rules:     - if: $SAST_DISABLED == 'true' || $SAST_DISABLED == '1'       when: never     - if: $SAST_EXCLUDED_ANALYZERS =~ /semgrep/       when: never     - if: $CI_COMMIT_BRANCH       exists:         - '**/*.py'         - '**/*.js'         - '**/*.jsx'         - '**/*.ts'         - '**/*.tsx'         - '**/*.c'         - '**/*.go'         - '**/*.java'         - '**/*.cs'         - '**/*.html'         - '**/*.scala'         - '**/*.sc'

Выше приведен пример описания задачи для semgrep. Нас тут интересует раздел rules, а если конкретнее, то:

  • блок exists, в котором идет перечисление масок, то есть для каких файлов применяется инструмент;

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

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

Видим аналогичную картину, как и в Secret Detection в предыдущей статье. Задачи отработали успешно, но в отчете есть уязвимости.

Опять же, стоит заметить, что в бесплатной версии очень мало функционала. 

Поэтому мы пойдем по тому же пути (что и в части 1) и немного допишем скрипт, кот

#!/bin/bash  vulnerability_count=$(cat $FILE_REPORT | jq --raw-output '.vulnerabilities | length') if [ ${vulnerability_count} -gt 0 ];  then   echo "|     severity     |     name     |     location     |     scanner     |"   echo "|------------------|--------------|------------------|-----------------|"   _jq() {    echo ${row} | base64 --decode | jq -r ${1}   }   for row in $(cat $FILE_REPORT | jq -r '.vulnerabilities[] | @base64'); do     vulnerability_name=$(_jq ".name")     if [ "$vulnerability_name" == "null" ]; then vulnerability_name=$(_jq ".message"); fi     echo '|' $(_jq ".severity") '|' $vulnerability_name '|' $(_jq ".location.file")':'$(_jq ".location.start_line") '|' $(_jq ".scanner.name") '|'   done fi  exit $vulnerability_count

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

В самом .gitlab-ci.yml подключаем этот отдельный файл с задачей.

stages:   - test  .analyzer_run:   script:     - apk add jq bash coreutils     - /analyzer run     - bash .gitlab/scripts/$NAME_OF_CI_SCRIPT.sh  include:   - local: '/.gitlab/templates/sast.gitlab-ci.yml'

Те же задачи, в которых найдены уязвимости, теперь падают:

Вывод уязвимостей в задаче Bandit (только Python):

Вывод уязвимостей в задаче Flawfinder (только C/C++):

Вывод уязвимостей в задаче Gosec (только Go):

Вывод уязвимостей в задаче Semgrep (поддерживает много языков, в т. ч. Python, Go, C/C++):

Выглядит интересно, SAST в Gitlab включается и настраивается легко. Есть много инструментов, которые могут дополнять друг друга.

Сложности при использовании SAST в Gitlab

  • Зоопарк технологий. Под каждый ЯП — свой набор инструментов, каждый из которых может работать по-своему. Например, phpcs-security-audit выполняется не под рутом, а это значит, что мы не можем установить свои либы (jq, bash и т. д.).

  • Ложные срабатывания — одна из главных проблем SAST-инструментов. А тут еще много инструментов, что кратно увеличивает вероятные проблемы.

Как решить эти проблемы 

Один из вариантов облегчения — исключить все лишние инструменты. Например, мы видим, что Semgrep покрывает достаточно много ЯП и в целом очень активно развивается. В этом нам поможет переменная SAST_EXCLUDED_ANALYZERS, которая позволяет исключать анализаторы.

sast:   variables:     SAST_EXCLUDED_ANALYZERS: "bandit,gosec,flawfinder" # можно исключать различные инструменты     FILE_REPORT: gl-sast-report.json     NAME_OF_CI_SCRIPT: "sast"   tags:     - docker

Тут мы исключаем Bandit, GoSec и Flawfinder, и выполняется только Semgrep. Таким образом, мы можем оставить какой-то один комплексный инструмент, который будет закрывать много языков, например тот же Semgrep. 

В целом и сам Gitlab потихоньку двигается в сторону уменьшения инструментов, объявляя, что перестает поддерживать некоторые из них (например, Bandit, ESLint, GoSec).

Подключение инструментов DevSecOps напрямую

До сих пор мы использовали только встроенные в GitLab-инструменты. В этом есть плюс — настройка разных шагов в CI/CD получается довольно похожей.

Но есть и минусы: 

  • GitLab может ограничивать возможности инструмента;

  • GitLab может не так активно актуализировать версии инструментов.

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

У Semgrep есть официальный образ в Docker Hub, и его можно использовать с минимальными доработками. Для этого:

  1. Добавляем новый шаблон.

semgrep:  stage: test  image: semgrep/semgrep  variables:    FILE_REPORT: gl-sast-report.json    SEMGREP_RULES: >-      p/security-audit      p/secrets      p/python      p/django      p/phpcs-security-audit  tags:    - docker  script:    - semgrep ci --gitlab-sast > $FILE_REPORT || true    - apk add jq bash coreutils    - bash .gitlab/scripts/sast.sh  artifacts:    reports:      sast: $FILE_REPORT
  • SEMGREP_RULES — это список правил, на соответствие которым проверяется весь исходный код. Для поиска правил можно использовать сервис: https://semgrep.dev/r 

  • —gitlab-sast — это специальный флаг, который формирует вывод в таком же формате, как в GitLab.

  • || true — по умолчанию при наличии ошибок данная утилита возвращает ошибку и от этого job завершается. Нам же нужно обработать файл с уязвимостями, поэтому подавляем эту ошибку.

  1. Включаем данный шаблон в .gtilab-ci.yml:

include:   - local: '/.gitlab/templates/sast-semgrep.gitlab-ci.yml'

Остановились на SonarQube

Если говорить о нашей компании, то мы в итоге остановились на другом инструменте — SonarQube. Этот инструмент уже не является частью GitLab ни в каком виде, но умеет интегрироваться с ним. 

SonarQube нам понравился своим удобным интерфейсом: это и удобная визуализация, и возможность быстрой реакции на найденные уязвимости (можно указать, что это корректное поведение, а можно — что это ложное срабатывание).

По данной ссылке содержится информация о том, как установить SonarQube и интегрировать его с GitLab.

Dependency Scanning

Другой класс инструментов Commit-time Checks — Dependency Scanning (это процесс автоматического обнаружения уязвимостей в зависимостях).

Как работают Dependency Scanning-инструменты

Во многих языках программирования есть пакетные менеджеры, при помощи которых мы можем выкачивать код, и информация об этом сохраняется в различных файлах (go.sum, composer.lock, package-lock.json, yarn.lock, Gemfile.lock, requirements.txt и т. д.). Dependency Scanning-инструменты сканируют эти файлы, смотрят, какие пакеты были выкачаны и какие были версии, далее пакеты проверяют в базе скомпрометированного ПО и, если их там находят, выдают ошибку.

Виды Dependency Scanning-инструментов

Dependency Scanning в GitLab

В GitLab есть инструмент Dependency Scanning, но, к сожалению, в бесплатной версии GitLab этот инструмент уже недоступен ни в каком виде.

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

В GitLab используется некий инструмент Gemnasium. Gemnasium — это собственное решение GitLab, при этом оно открытое и его можно использовать. Вернее, с одной стороны, мы его не можем использовать, потому что он в Ultimate-версии, но можем использовать его образ, чтобы использовать в бесплатной версии. Ниже приведен пример того, как это выглядит в исходниках: 

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

dependency_scanning_custom:   stage: test   variables:     FILE_REPORT: gl-dependency-scanning-report.json     NAME_OF_CI_SCRIPT: "dependency_scanning"   image: registry.gitlab.com/gitlab-org/security-products/analyzers/gemnasium:3   tags:     - docker   artifacts:     reports:       dependency_scanning: gl-dependency-scanning-report.json   script:     - !reference [.analyzer_run, script]  

Создаем скрипт обработки артефакта

#!/bin/bash  vulnerability_count=$(cat $FILE_REPORT | jq --raw-output '.vulnerabilities | length') if [ ${vulnerability_count} -gt 0 ];  then   echo "|     severity     |     name     |     file     |     package     |"   echo "|------------------|--------------|--------------|-----------------|"   _jq() {    echo ${row} | base64 --decode | jq -r ${1}   }   for row in $(cat $FILE_REPORT | jq -r '.vulnerabilities[] | @base64'); do     echo '|' $(_jq ".severity") '|' $(_jq ".name") '|' $(_jq ".location.file") '|' $(_jq ".location.dependency.package.name") '|'   done fi  exit $vulnerability_count 

Закидываем какие-то пакеты, например, в composer и go.

Видим отчет Dependency Scanning: обнаружены 2 пакета, уязвимостей нет. 

У Gemnasium есть отдельный сайт с поиском по БД-уязвимостей. Можно найти какой-то скомпрометированный пакет, выбрать его и включить в пакетный менеджер.

Ниже приведен пример уязвимости: 

Добавляем несколько пакетов с уязвимостями — уязвимости в задаче найдены, выводится информация об этом в задаче:

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

Итоги

Итак, мы разобрали еще один этап в процессе DevSecOps — Commit-time Checks. Все наработки по коду лежат в этом репозитории.

Следующая часть статьи будет посвящена стадии Post-build Checks.


ссылка на оригинал статьи https://habr.com/ru/articles/817267/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *