Статический анализ кода

Статический анализ кода

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

Изображение записи

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

Ревью кода

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

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

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

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

Ограничения статического анализа

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

Как правило, если возникает проблема, которую сложно обнаружить с помощью инструментов, лучшим решением будет изменение архитектуры, чтобы исключить саму возможность ее появления. К примеру, можно выбрать язык, безопасный для работы с памятью (memory-safe language), или спроектировать компонент, который допускает только заведомо безопасные запросы.

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

Универсальные инструменты поиска ошибок

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

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

Хотя эти инструменты редко сфокусированы на безопасности, они все равно помогают повысить защищенность ПО. Важно учитывать, что:

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

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

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

Инструменты статического анализа (SAST)

Некоторые инструменты анализируют код целенаправленно для поиска уязвимостей. Они известны под разными названиями: инструменты статического тестирования безопасности приложений (Static Application Security Testing, SAST), сканеры безопасности кода или просто статические анализаторы кода. 

Некоторые специалисты используют термин SAST только для инструментов, которые анализируют исходный код, но в курсе мы не будем ограничивать это понятие. Подробнее о SAST можно почитать в статьях “The AppSec alphabet soup: A guide to SAST, IAST, DAST, and RASP Фреда Балса, 2018” и “10 Types of Application Security Testing Tools: When and How to Use Them”.

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

Разные инструменты ищут разные шаблоны

Каждый инструмент использует свои эвристики. Один инструмент найдет одни уязвимости, другой — другие. Нельзя полагаться на один инструмент для обнаружения всех уязвимостей.

Ограничения по языкам и средам

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

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

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

Зачастую правильнее всего исправить проблему при любой ситуации. Утверждения, что что-то «не может произойти», часто оказываются ошибочными, а ПО или его окружение могут измениться в будущем. Исправление помогает обеспечить долгосрочную устойчивость кода.

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

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

ReDoS-атаки (отказ обслуживания через регулярные выражения)

Инструменты обнаруживают регулярные выражения, которые при определенных входных данных могут вызывать катастрофическое замедление работы приложения. В их описании часто встречаются термины ReDoS, «уязвимые регулярные выражения» (evil regex) и «безопасные регулярные выражения» (safe regex). Принцип работы заключается в извлечении регулярных выражений из исходного кода и анализе их на потенциальную уязвимость к ReDoS.

Жестко заданные учетные данные (Hardcoded Credentials)

Инструменты ищут в коде жестко прописанные криптографические ключи, пароли, токены и другие конфиденциальные данные. Их часто называют сканерами конфиденциальных данных или сканерами секретов (secret scanners).

Инструменты композиционного анализа (SCA)

Существует тип статического анализа, который известен под разными названиями: анализ состава ПО (Software Composition Analysis, SCA), анализ зависимостей (dependency analysis) и анализ происхождения компонентов (origin analysis). Разберемся, почему он так важен.

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

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

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

Поскольку большая часть повторно используемого кода — это открытое ПО (Open Source Software, OSS), многие называют такой анализ «анализом открытого ПО». Это не совсем верно, поскольку проблема касается любого стороннего кода, но фокус на открытом ПО понятен, ведь именно оно составляет основную массу зависимостей.

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

Существуют открытые базы данных, содержащие информацию о ПО с известными уязвимостями. Особенно широко используется Национальная база данных уязвимостей (National Vulnerability Database, NVD), поддерживаемая NIST. Это общедоступный каталог публично известных уязвимостей, каждая из которых определяется по CVE-идентификатору и связана с продуктами и версиями, в которых эта уязвимость присутствует. Некоторые коммерческие поставщики также ведут собственные базы данных.

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

Однако на практике это не всегда просто.

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

Поэтому SCA-инструменты нужно запускать регулярно. Новые уязвимости появляются постоянно, в том числе в ранее «чистых» компонентах.