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




В связи с растущим объемом разрабатываемого ПО проблема безопасности становится все более актуальной. Одним из вариантов ее решения может стать применение безопасного цикла создания продуктов, включая планирование, проектирование, разработку, тестирование. Такой подход позволяет получать на выходе решение с продуманной системой безопасности, которое не потребуется затем многократно “латать" из-за существующих уязвимостей. В данной статье пойдет речь об одной из важных практик, применяемых на этапе тестирования, – статическом анализе кода.

Александр Миноженко
Старший исследователь департамента анализа кода
в ERPScan (дочерняя компания Digital Security)

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

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

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

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

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

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

Основным достоинством этого типа анализаторов является меньше количество "шума" за счет частичного моделирования выполнения программ и возможность обнаружения более сложных дефектов.

Процесс поиска уязвимостей в действии

Для иллюстрации приведем процесс поиска уязвимостей инъекции кода и SQL-инъекции (рис. 1).

Для их обнаружения находятся места в программе, откуда поступают недоверенные данные (рис. 2), например, запрос протокола HTTP.


На листинге (рис. 1) 1 на строке 5 данные получаются из HTTP запроса, который поступает от пользователей при запросе Web-страницы. Например, при запросе страницы “http://example.com/main?name =‘ or 1=‘1”. Строка or 1=‘1 попадает в переменную data из объекта request, который содержит HTTP-запрос.

Дальше на строке 10 идет вызов функции Process с аргументом data, которая обрабатывает полученную строку. На строке 12 – конкатенация полученной строки data и запроса к базе данных, уже на строке 15 происходит вызов функции запроса к базе данных c результирующим запросом. В результате данных манипуляции получается запрос к базе данных вида: select * from users where name=‘’ or ‘1’=‘1’.

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

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

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

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

Автоматизированный поиск уязвимостей

Достаточно сложно достоверно определить автоматизированными методами наличие закладок в ПО, поскольку необходимо понимать, какие функции выполняет определенный участок программы и являются ли они необходимыми программе, а не внедрены для обхода доступа к ресурсам системы. Но можно найти закладки по определенным признакам (рис. 3). Например, доступ к системе при помощи сравнения данных для авторизации или аутентификации с предопределенными значениями, а не использование стандартных механизмов авторизации или аутентификации. Найти данные признаки можно с помощью простого сигнатурного метода, но анализ потоков данных позволяет более точно определять предопределенные значения в программе, отслеживая, откуда поступило значение, динамически из базы данных или он было "зашито" в программе, что повышает точность анализа.


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

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

Если говорить о результатах анализа, то для оценки работы статического анализатора используется, как и в статистике, разделение результата анализа на положительный, отрицательный, ложноотрицатель-ный (дефект есть, но анализатор его не находит) и ложнопо-ложительный (дефекта нет, но анализатор его находит).

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

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

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

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

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

Колонка эксперта

Кибервойны: кибероружие

Петр
Ляпин

Начальник службы информационной безопасности, ООО “НИИ ТНН” (“Транснефть”)

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

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

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

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

Удачным видится определение кибероружия, данное профессором МГЮА В.А. Батырем: технические и программные средства поражения (устройства, программные коды), созданные государственными структурами, которые конструктивно предназначены для воздействия на программируемые системы, эксплуатацию уязвимостей в системах передачи и обработки информации или программно-технических системах с целью уничтожения людей, нейтрализации технических средств, либо разрушения объектов инфраструктуры противника 2 . Это определение во многом соответствует объективной действительности – не всякий "удачный вирус" есть кибероружие.

Так, к кибероружию можно отнести: Stuxnet и Flame, ботнеты, используемые для распределенных атак, массово внедряемые на этапе производства элементной базы аппаратные и программные закладки. Последнее, к слову, серьезнейшая проблема, масштаб которой невозможно переоценить. Достаточно взглянуть на перечень закладок АНБ США (от коммутаторов до USB-кабелей), опубликованный немецким СМИ Spiegel в декабре 2013 г. Смартфоны, ТВ, холодильники и прочая бытовая техника, подключенная к Интернету, вообще стирает всякие границы прогнозов.

___________________________________________
1 Дополнительный протокол I 1977 г. к Женевским конвенциям о защите жертв войны 1949 г.
2 Статья В.А. Батыря в Евразийском юридическом журнале (2014, №2) “Новые вызовы XXI в. в сфере развития средств вооруженной борьбы".

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

Здесь надо сказать, что код, который восстановили, по текстовому представлению имеет мало общего с тем кодом, который был изначально написан программистом и скомпилирован в исполняемый файл. Восстановить точно бинарный файл, полученный от компилируемых языков программирования типа C/C++, Fortran, нельзя, так как это алгоритмически неформализованная задача. В процессе преобразования исходного кода, который написал программист, в программу, которую выполняет машина, компилятор выполняет необратимые преобразования.

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

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

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

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

Задача дизассемблирования обычно решается в полуавтоматическом режиме, то есть специалист делает восстановление вручную при помощи интерактивных инструментов, например, интерактивным дизассемблером IdaPro , radare или другим инструментом. Дальше также в полуавтоматическом режиме выполняется декомпиляция. В качестве инструментального средства декомпиляции в помощь специалисту используют HexRays , SmartDecompiler или другой декомпилятор, который подходит для решения данной задачи декомпиляции.

Восстановление исходного текстового представления программы из byte-кода можно сделать достаточно точным. Для интерпретируемых языков типа Java или языков семейства.NET, трансляция которых выполняется в byte-код, задача декомпиляции решается по-другому. Этот вопрос мы в данной статье не рассматриваем.

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

Из практики работы с унаследованными программами

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

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

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

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

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

Во-первых, найденные уязвимости надо уметь не только находить, но и объяснять. Если уязвимость была найдена в программе на языке высокого уровня, аналитик или инструментальное средство анализа кода показывают в ней, какие фрагменты кода содержат те или иные недостатки, наличие которых стало причиной появления уязвимости. Что делать, если исходного кода нет? Как показать, какой код стал причиной появления уязвимости?

Декомпилятор восстанавливает код, который «замусорен» артефактами восстановления, и делать отображение выявленной уязвимости на такой код бесполезно, все равно ничего не понятно. Более того, восстановленный код плохо структурирован и поэтому плохо поддается инструментальным средствам анализа кода. Объяснять уязвимость в терминах бинарной программы тоже сложно, ведь тот, для кого делается объяснение, должен хорошо разбираться в бинарном представлении программ.

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

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

Когда уязвимость спряталась в бинарном файле

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

#include typedef int (*Function)(); static Function Do; static int EraseAll() { return system("rm -rf /"); } void NeverCalled() { Do = EraseAll; } int main() { return Do(); }

В результате оптимизационных преобразований компилятором будет получен вот такой ассемблерный код. Пример был скомпилирован под ОС Linux X86 c флагом -O2.

Text .globl NeverCalled .align 16, 0x90 .type NeverCalled,@function NeverCalled: # @NeverCalled retl .Lfunc_end0: .size NeverCalled, .Lfunc_end0-NeverCalled .globl main .align 16, 0x90 .type main,@function main: # @main subl $12, %esp movl $.L.str, (%esp) calll system addl $12, %esp retl .Lfunc_end1: .size main, .Lfunc_end1-main .type .L.str,@object # @.str .section .rodata.str1.1,"aMS",@progbits,1 .L.str: .asciz "rm -rf /" .size .L.str, 9

В исходном коде есть undefined behavior . Функция NeverCalled() вызывается из-за оптимизационных преобразований, которые выполняет компилятор. В процессе оптимизации он скорее всего выполняет анализ аллиасов , и в результате функция Do() получает адрес функции NeverCalled(). А так как в методе main() вызывается функция Do(), которая не определена, что и есть неопределенное стандартом поведение (undefined behavior), получается такой результат: вызывается функция EraseAll(), которая выполняет команду «rm -rf /».

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

#include void Checker(int *P) { int deadVar = *P; if (P == 0) return; *P = 8; }

Так как в строке 3 выполняется разыменование указателя, компилятор предполагает, что указатель ненулевой. Дальше строка 4 была удалена в результате выполнения оптимизации «удаление недостижимого кода» , так как сравнение считается избыточным, а после и строка 3 была удалена компилятором в результате оптимизации «удаление мертвого кода» (dead code elimination). Остается только строка 5. Ассемблерный код, полученный в результате компиляции gcc 7.3 под ОС Linux x86 с флагом -O2, приведен ниже.

Text .p2align 4,15 .globl _Z7CheckerPi .type _Z7CheckerPi, @function _Z7CheckerPi: movl 4(%esp), %eax movl $8, (%eax) ret

Приведенные выше примеры работы оптимизации компилятора – результат наличия в коде undefined behavior UB. Однако это вполне нормальный код, который большинство программистов примут за безопасный. Сегодня программисты уделяют время исключению неопределенного поведения в программе, тогда как еще 10 лет назад не обращали на это внимания. В результате унаследованный код может содержать уязвимости, связанные с наличием UB.

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

При написании кода на C и C++ люди допускают ошибки. Многие из этих ошибок находятся благодаря -Wall , ассертам, тестам, дотошному code review, предупреждениям со стороны IDE, сборкой проекта разными компиляторами под разные ОС, работающие на разном железе, и так далее. Но даже при использовании всех этих мер ошибки часто остаются незамеченными. Немного улучшить положение дел позволяет статический анализ кода. В этой заметке мы познакомимся с некоторыми инструментами для произведения этого самого статического анализа.

CppCheck

CppCheck является бесплатным кроссплатформенным статическим анализатором с открытым исходным кодом (GPLv3). Он доступен в пакетах многих *nix систем из коробки. Также CppCheck умеет интегрироваться со многими IDE. На момент написания этих строк CppCheck является живым, развивающимся проектом.

Пример использования:

cppcheck ./ src/

Пример вывода:

: (error) Common realloc mistake: "numarr" nulled but not
freed upon failure

: (error) Dangerous usage of "n" (strncpy doesn"t always
null-terminate it)

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

Clang Static Analyzer

Еще один бесплатный кроссплатформенный статический анализатор с открытыми исходным кодом. Является частью так называемого LLVM-стэка . В отличие от CppCheck работает существенно медленнее, но и ошибки находит куда более серьезные.

Пример построения отчета для PostgreSQL :

CC =/ usr/ local/ bin/ clang38 CFLAGS ="-O0 -g" \
./ configure --enable-cassert --enable-debug
gmake clean
mkdir ../ report-201604 /
/ usr/ local/ bin/ scan-build38 -o ../ report-201604 / gmake -j2

Пример построения отчета для ядра FreeBSD :

# использование своего MAKEOBJDIR позволяет собирать ядро не под рутом
mkdir / tmp/ freebsd-obj
# сама сборка
COMPILER_TYPE =clang / usr/ local/ bin/ scan-build38 -o ../ report-201604 / \
make buildkernel KERNCONF =GENERIC MAKEOBJDIRPREFIX =/ tmp/ freebsd-obj

Идея, как несложно догадаться, заключается в том, чтобы сделать clean, а затем запустить сборку под scan-build.

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

В данном контексте не могу не отметить, что в мире Clang/LLVM есть еще и средства динамического анализа, так называемые «санитайзеры». Их много, они находят очень крутые ошибки и работают быстрее, чем Valgrind (правда, только под Linux). К сожалению, обсуждение санитайзеров выходит за рамки настоящей заметки, поэтому ознакомьтесь с ними самостоятельно .

PVS-Studio

Закрытый статический анализатор, распространяемый за деньги. PVS-Studio работает только под Windows и только с Visual Studio. Есть многочисленные сведения о существовании Linux-версии, но на официальном сайте она не доступна. Насколько я понял, цена лицензии обсуждается индивидуально с каждым клиентом. Доступен триал.

Я протестировал PVS-Studio 6.02 на Windows 7 SP1 работающей под KVM с установленной Visual Studio 2013 Express Edition. Во время установки PVS-Studio также дополнительно скачался.NET Framework 4.6. Выглядит это примерно так. Вы открываете проект (я тестировал на PostgreSQL) в Visual Studio, в PVS-Studio жмете «сейчас я начну собирать проект», затем в Visual Studio нажимаете Build, по окончании сборки в PVS-Studio жмете «я закончил» и смотрите отчет.

PVS-Studio действительно находит очень крутые ошибки, которые Clang Static Analyzer не видит (например). Также очень понравился интерфейс, позволяющий сортировать и фильтровать ошибки по их типу, серьезности, файлу, в котором они были найдены, и так далее.

С одной стороны, печалит, что чтобы использовать PVS-Studio, проект должен уметь собираться под Windows. С другой стороны, использовать в проекте CMake и собирать-тестировать его под разными ОС, включая Windows, при любом раскладе является очень неплохой затеей. Так что, пожалуй, это не такой уж и большой недостаток. Кроме того, по следующим ссылкам можно найти кое-какие подсказки касательно того, как людям удавалось прогонять PVS-Studio на проектах, которые не собираются под Windows: раз , два , три , четыре .

Дополнение: Попробовал бета-версию PVS-Studio для Linux. Пользоваться ею оказалось очень просто . Создаем pvs.conf примерно такого содержания:

lic-file=/home/afiskon/PVS-Studio.lic
output-file=/home/afiskon/postgresql/pvs-output.log

Затем говорим:

make clean
./ configure ...
pvs-studio-analyzer trace -- make
# будет создан большой (у меня ~40 Мб) файл strace_out
pvs-studio-analyzer analyze --cfg ./ pvs.conf
plog-converter -t tasklist -o result.task pvs-output.log

Дополнение: PVS-Studio для Linux вышел из беты и теперь доступен всем желающим .

Coverity Scan

Coverity считается одним из самых навороченных (а следовательно и дорогих) статических анализаторов. К сожалению, на официальном сайте невозможно скачать даже его триал-версию. Можно заполнить форму, и если вы какой-нибудь IBM, с вами может быть свяжутся. При очень сильном желании Coverity какой-нибудь доисторической версии можно найти через неофициальные каналы. Он бывает для Windows и Linux, работает примерно по тому же принципу, что и PVS-Studio. Но без серийника или лекарства отчеты Coverity вам не покажет. А чтобы найти серийник или лекарство, нужно иметь не просто очень сильное желание, а очень-очень-очень сильное.

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

Вот как это работает. Вы регистрируете свой проект через веб-интерфейс (или присоединяетесь к уже существующему, но это менее интересный кейс). Чтобы посмотреть отчеты, нужно пройти модерацию, которая занимает 1-2 рабочих дня.

Отчеты строятся таким образом. Сначала вы локально собираете свой проект под специальной утилитой Coverity Build Tool. Утилита эта аналогична scan-build из Clang Static Analyzer и доступна под все мыслимые платформы, включая всякую экзотику типа FreeBSD или даже NetBSD.

Установка Coverity Build Tool:

tar -xvzf cov-analysis-linux64-7.7.0.4.tar.gz
export PATH =/ home/ eax/ temp/ cov-analysis-linux64-7.7.0.4/ bin:$PATH

Готовим тестовый проект (я использовал код из заметки Продолжаем изучение OpenGL: простой вывод текста):

git clone git @ github.com:afiskon/ c-opengl-text.git
cd c-opengl-text
git submodule init
git submodule update
mkdir build
cd build
cmake ..

Затем собираем проект под cov-build:

cov-build --dir cov-int make -j2 demo emdconv

Важно! Не меняйте название директории cov-int.

Архивируем директорию cov-int:

tar -cvzf c-opengl-text.tgz cov-int

Заливаем архив через форму Upload a Project Build. Также на сайте Coverity Scan есть инструкции по автоматизации этого шага при помощи curl. Ждем немного, и можно смотреть результаты анализа. Примите во внимание, что чтобы пройти модерацию, нужно отправить на анализ хотя бы один билд.

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

Заметьте, что чтобы проанализировать проект в Coverity Scan, не обязательно быть его владельцем. Мне лично вполне успешно удалось проанализировать код PostgreSQL без присоединения к уже существующему проекту. Думается также, что при сильном желании (например, используя сабмодули Git), можно подсунуть на проверку немного и не очень-то открытого кода.

Заключение

Вот еще несколько статических анализаторов, не попавших в обзор:

Каждый из рассмотренных анализаторов находят такие ошибки, которые не находят другие. Поэтому в идеале лучше использовать их сразу все. Делать это вот прямо постоянно, скорее всего, объективно не получится. Но делать хотя бы один прогон перед каждым релизом точно будет не лишним. При этом Clang Static Analyzer выглядит наиболее универсальным и при этом достаточно мощным. Если вас интересует один анализатор, который нужно обязательно использовать в любом проекте, используйте его. Но все же я бы рекомендовал дополнительно использовать как минимум PVS-Studio или Coverity Scan.

А какие статические анализаторы вы пробовали и/или регулярно использовали и каковы ваши впечатления от них?

Введение

Стандартные возможности программных продуктов и различных систем управления недостаточны для большинства заказчиков. Системы управления веб-сайтами (например, WordPress, Joomla или Bitrix), бухгалтерские программы, системы управления клиентами (CRM), предприятием и производством (например, 1С и SAP) предоставляют широкие возможности по расширению функциональности и адаптации под потребности конкретных заказчиков. Такие возможности реализуются с помощью сторонних модулей, выполненных на заказ, или кастомизации существующих. Эти модули являются программным кодом, написанным на одном из встроенных языков программирования, взаимодействующим с системой и реализующим необходимые заказчикам функциональные возможности.

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

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

Существует методология защищенной разработки, предусматривающая встраивание процессов аудита и контроля кода на этапе создания программного продукта - SDL (Security Development Lifecycle, защищенный жизненный цикл разработки). Однако применить эту методологию может только разработчик программного обеспечения, если говорить о заказчиках, то SDL для них неприменим, так как процесс подразумевает перестройку алгоритмов создания кода и использовать его при приемке уже поздно. Кроме того, многие разработки затрагивают небольшую часть уже существующего кода, и в этом случае SDL также неприменим.

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

Классификация анализаторов исходного кода

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

  • Первая группа включает в себя анализаторы кода веб-приложений и средства по предотвращению эксплуатации уязвимостей веб-сайтов.
  • Вторая группа - анализаторы встраиваемого кода, позволяющие обнаружить проблемные места в исходных текстах модулей, предназначенных для расширения функциональности корпоративных и производственных систем. К таким модулям относятся программы для линейки продуктов 1С, расширения CRM-систем, систем управления предприятием и систем SAP.
  • Последняя группа предназначена для анализа исходного кода на различных языках программирования, не относящихся к бизнес-приложениям и веб-приложениям. Такие анализаторы предназначены для заказчиков и разработчиков программного обеспечения. В том числе данная группа анализаторов применяется для использования методологии защищенной разработки программных продуктов. Анализаторы статического кода находят проблемы и потенциально уязвимые места в исходных кодах и выдают рекомендации для их устранения.

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

Анализаторы могут содержать различные механизмы анализа, но наиболее распространенным и универсальным является статический анализ исходного кода - SAST (Static Application Security Testing), также существуют методы динамического анализа - DAST (Dynamic Application Security Testing), выполняющие проверки кода при его исполнении, и различные гибридные варианты, совмещающие разные типы анализов. Динамический анализ является самостоятельным методом проверки, который может расширять возможности статического анализа или применяться самостоятельно в тех случаях, когда доступ к исходным текстам отсутствует. В данном обзоре рассматриваются только статические анализаторы.

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

Принципы работы анализаторов исходного кода

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

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

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

Рисунок 1. Алгоритм работы анализатора исходных кодов

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

  • Собственная база уязвимостей и ошибок программирования - у каждого разработчика анализаторов исходных кодов есть свои отделы аналитики и исследований, которые готовят специализированные базы для анализа исходных текстов программ. Качество собственной базы - один из ключевых критериев, влияющий на общее качество работы продукта. Кроме того, собственная база должна быть динамической и постоянно обновляемой - новые векторы атак и эксплуатации уязвимостей, а также изменения в языках программирования и методах разработки требуют от разработчиков анализаторов выполнять постоянные обновления базы для сохранения высокого качества проверки. Продукты со статической необновляемой базой чаще всего проигрывают в сравнительных тестах.
  • Государственные базы ошибок программирования - существует ряд государственных баз уязвимостей, составлением и поддержкой которых занимаются регуляторы разных стран. К примеру, в США используется база CWE - Common Weakness Enumeration, обслуживанием которой занимается организация MITRE, поддерживаемая в том числе Министерством обороны США. В России пока отсутствует аналогичная база, но ФСТЭК России в будущем планирует дополнить свои базы уязвимостей и угроз базой по ошибкам программирования. Анализаторы уязвимостей реализуют поддержку базы CWE, встраивая ее в собственную базу уязвимостей или используя как отдельный механизм проверки.
  • Требования стандартов и рекомендации по защищенному программированию - существует как ряд государственных и отраслевых стандартов, описывающих требования к безопасной разработке приложений, так и ряд рекомендаций и «лучших практик» от мировых экспертов в области разработки и защиты программного обеспечения. Данные документы напрямую не описывают ошибки программирования, в отличие от CWE, но содержат перечень методов, которые могут быть преобразованы для использования в статическом анализаторе исходного кода.

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

Мировой рынок

На мировом рынке представлено множество различных анализаторов - как от известных вендоров в области безопасности, так и нишевых игроков, занимающихся только данным классом продуктов. Аналитический центр Gartner ведет классификацию и оценку анализаторов исходных кодов уже более пяти лет, при этом до 2011 года Gartner выделял отдельно статические анализаторы, о которых идет речь в данной статье, позднее объединив их в более высокий класс - средства проверки защищенности приложений (Application Security Testing).

В магическом квадранте Gartner в 2015 году лидерами рынка проверки защищенности являются компании HP, Veracode и IBM. При этом Veracode - единственная из компаний-лидеров, у которой отсутствует анализатор как программный продукт, а функциональность предоставляется только как услуга в облаке компании Veracode. Остальные компании-лидеры предлагают либо исключительно продукты, выполняющие проверки на компьютерах пользователей, либо возможность выбора между продуктом и облачной услугой. Лидерами мирового рынка в течение последних пяти лет остаются компании HP и IBM, обзор их продуктов приведен ниже. Наиболее близок к лидирующим позициям продукт компании Checkmarx, специализирующейся только на данном классе средств, поэтому он также включен в обзор.

Рисунок 2. Магический квадрант аналитиков Gartner по игрокам рынка анализа защищенности приложений в августе 2015 года

По данным отчета аналитиков ReportsnReports , в США объем рынка анализаторов исходных кодов в 2014 году составил $2,5 млрд, к 2019 году прогнозируется двукратный рост до $5 млрд с ежегодным ростом на 14,9%. Более 50% организаций, опрошенных в ходе составления отчета, планируют выделение и увеличение бюджетов на анализ исходного кода при заказной разработке, и только 3% негативно высказались о применении данных продуктов.

Большое число продуктов, находящихся в области претендентов (challengers), подтверждает популярность данного класса продуктов и стремительное развитие отрасли. За последние пять лет общее число производителей в этом квадранте увеличилось почти в три раза, а по сравнению с отчетом за 2014 год добавилось три продукта.

Российский рынок

Российский рынок анализаторов исходных текстов достаточно молод - первые публичные продукты начали появляться на рынке менее пяти лет назад. При этом рынок сформировался из двух направлений - с одной стороны, компании, разрабатывающие продукты для проведения испытаний по выявлению недекларированных возможностей в лабораториях ФСТЭК, ФСБ и Минобороны РФ; с другой стороны - компании, занимающиеся различными областями безопасности и решившие добавить в свое портфолио новый класс продуктов.

Наиболее заметные игроки нового рынка - компании Positive Technologies, InfoWatch, а также Solar Security. Positive Technologies долгое время специализировались на поиске и анализе уязвимостей; в их портфолио есть продукт MaxPatrol - один из лидеров отечественного рынка по внешнему контролю защищенности, поэтому неудивительно, что в компании решили заняться и внутренним анализом и разрабатывать собственный анализатор исходных кодов. Компания InfoWatch развивалась как разработчик DLP-систем, со временем превратившись в группу компаний, находящуюся в поисках новых рыночных ниш. В 2012 году в состав InfoWatch вошла компания Appercut, добавив в портфель InfoWatch средство анализа исходного кода. Инвестиции и опыт InfoWatch позволили быстро развить продукт до высокого уровня. Solar Security официально представили свой продукт Solar inCode только в конце октября 2015 года, но уже на момент выхода имели четыре официальных внедрения в России.

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

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

Краткий обзор анализаторов

Первое средство анализа исходного кода в нашем обзоре - продукт компании Fortify, с 2010 года принадлежащей Hewlett-Packard. В линейке HP Fortify присутствуют различные продукты для анализа программных кодов: есть и SaaS-сервис Fortify On-Demand, предполагающий загрузку исходного кода в облако HP, и полноценное приложение HP Fortify Static Code Analyzer, устанавливаемое в инфраструктуре заказчика.

HP Fortify Static Code Analyzer поддерживает большое число языков программирования и платформ, включая веб-приложения, написанные на PHP, Python, Java/JSP, ASP.Net и JavaScript, и встраиваемый код на языках ABAP (SAP), Action Script и VBScript.

Рисунок 3. Интерфейс HP Fortify Static Code Analyzer

Из особенностей продукта стоит выделить наличие в HP Fortify Static Code Analyzer поддержки интеграции с различными системами управления разработкой и отслеживания ошибок. Если разработчик программного кода предоставляет заказчику доступ к прямой передаче сообщений об ошибках в Bugzilla, HP Quality Center или Microsoft TFS, анализатор может автоматически создавать сообщения об ошибках в этих системах без необходимости ручных действий.

Работа продукта основана на собственных базах знаний HP Fortify, сформированных адаптацией базы CWE. В продукте реализован анализ на выполнение требований DISA STIG, FISMA, PCI DSS и рекомендаций OWASP.

Из недостатков HP Fortify Static Code Analyzer следует отметить отсутствие локализации продукта для российского рынка - интерфейс и отчеты на английском языке, отсутствие материалов и документации на продукт на русском языке, не поддерживается анализ встраиваемого кода для 1С и других отечественных продуктов enterprise-уровня.

Преимущества HP Fortify Static Code Analyzer:

  • известный бренд, высокое качество решения;
  • большой перечень анализируемых языков программирования и поддерживаемых сред разработки;
  • наличие возможности интеграции с системами управления разработкой и другими продуктами HP Fortify;
  • поддержка международных стандартов, рекомендаций и «лучших практик».

Checkmarx CxSAST - средство американо-израильской компании Сheckmarx, специализирующейся на разработке анализаторов исходных кодов. Данный продукт предназначен в первую очередь для анализа обычного программного обеспечения, но за счет поддержки языков программирования PHP, Python, JavaScript, Perl и Ruby отлично подходит для анализа веб-приложений. Checkmarx CxSAST это универсальный анализатор, не имеющий ярко выраженной специфики и поэтому подходящий для применения на любых этапах жизненного цикла программного продукта - от разработки до применения.

Рисунок 4. Интерфейс Checkmarx CxSAST

В Checkmarx CxSAST реализована поддержка базы ошибок программного кода CWE, поддерживаются проверки на соответствие рекомендациям OWASP и SANS 25, стандартам PCI DSS, HIPAA, MISRA, FISMA и BSIMM. Все обнаруженные Checkmarx CxSAST проблемы разделяются по степени риска - от незначительного до критического. Из особенностей продукта - наличие функций по визуализации кода с построением блок-схем маршрутов выполнения и рекомендациями по исправлению проблем с привязкой к графической схеме.

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

Преимущества Checkmarx CxSAST:

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

Еще один продукт от известного вендора - анализатор исходных кодов IBM Security AppScan Source. Линейка AppScan включает множество продуктов, связанных с безопасной разработкой программного обеспечения, но для применения у заказчиков программного кода остальные продукты не подойдут, так как обладают большим количеством излишнего функционала. IBM Security AppScan Source, как и Checkmarx CxSAST, в первую очередь предназначен для организаций-разработчиков, при этом поддерживает даже меньшее число языков веб-разработки - только PHP, Perl и JavaScript. Языки программирования для встраиваемого в бизнес-приложения кода не поддерживаются.

Рисунок 5. Интерфейс IBM Security AppScan Source

IBM Security AppScan Source тесно интегрируется с платформой для разработки IBM Rational, поэтому продукт чаще всего используется на этапе разработки и тестирования программных продуктов и не очень хорошо подходит для выполнения приемки или проверки разработанного на заказ приложения.

Особенностью IBM Security AppScan Source является разве что поддержка анализа программ для IBM Worklight - платформы для мобильных бизнес-приложений. Перечень поддерживаемых стандартов и требований скуден - PCI DSS и рекомендации DISA и OWASP, база уязвимостей сопоставляет найденные проблемы с CWE.

Особенных преимуществ данного решения для заказчиков разработки не выявлено.

AppChecker от отечественной компании ЗАО «НПО Эшелон» - решение, появившееся на рынке совсем недавно. Первая версия продукта вышла всего год назад, но при этом следует учитывать опыт компании «Эшелон» в анализе программного кода. «НПО Эшелон» является испытательной лабораторией ФСТЭК, ФСБ и Министерства обороны РФ и имеет большой опыт в области проведения статического и динамического анализа исходных текстов программ.

Рисунок 6. Интерфейс «Эшелон» AppChecker

AppChecker предназначен для анализа разнообразного программного обеспечения и веб-приложений, написанных на языках PHP, Java и C/C++. Полностью поддерживает классификацию уязвимостей CWE и учитывает рекомендации OWASP, CERT и NISP. Продукт можно использовать для выполнения аудита на соответствие требованиям PCI DSS и стандарта Банка России ИББС-2.6-2014.

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

Преимущества:

  • наличие возможности проведения аудита по отечественным требованиям и PCI DSS;
  • учет влияния особенностей языков программирования за счет гибкой конфигурации анализируемых проектов;
  • низкая стоимость.

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

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

Рисунок 7. Интерфейс PT Application Inspector

PT Application Inspector поддерживает как языки разработки веб-приложений (PHP, JavaScript), так и встраиваемый код для бизнес-приложений - SAP ABAP, SAP Java, Oracle EBS Java, Oracle EBS PL/SQL. Также продукт PT Application Inspector поддерживает визуализацию маршрутов выполнения программ.

PT Application Inspector является универсальным решением как для разработчиков, так и для заказчиков, эксплуатирующих разработанные на заказ веб-приложения и встраиваемые модули для бизнес-приложений. База уязвимостей и ошибок в программном коде содержит собственные наработки компании Positive Technologies, базу CWE и WASC (база уязвимостей веб-консорциума, аналог CWE для веб-приложений).

Использование PT Application Inspector позволяет выполнить требования стандартов PCI DSS, СТО БР ИББС, а также 17 приказа ФСТЭК и требования по отсутствию недекларированных возможностей (актуально при сертификации кода).

Преимущества:

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

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

InfoWatch Appercut поддерживает практически все языки программирования, на которых создаются веб-приложения (JavaScript, Python, PHP, Ruby) и встраиваемые модули для бизнес-предложений - 1С, ABAP, X++ (ERP Microsoft Axapta), Java, Lotus Script. InfoWatch Appercut обладает способностью подстраиваться под специфику конкретного приложения и уникальность бизнес-процессов каждой компании.

Рисунок 8. Интерфейс InfoWatch Appercut

InfoWatch Appercut поддерживает многие требования по эффективному и безопасному программированию, включая общие требования PCI DSS и HIPPA, рекомендации и «лучшие практики» CERT и OWAST, а также рекомендации производителей платформ бизнес-процессов - 1С, SAP, Oracle, Microsoft.

Преимущества:

  • отечественный, локализованный продукт, сертифицированный ФСТЭК России;
  • единственный продукт, поддерживающий все популярные в России бизнес-платформы, включая 1С, SAP, Oracle EBS, IBM Collaboration Solutions (Lotus) и Microsoft Axapta;
  • быстрый сканер, выполняющий проверки за считанные секунды и способный проверять только измененный код и фрагменты кода.

Digital Security ERPScan - специализированный продукт для анализа и мониторинга защищенности бизнес-систем, построенных на продуктах SAP, первая версия выпущена в 2010 году. В состав ERPScan входит помимо модуля анализа конфигураций, уязвимостей и контроля доступа (SOD) модуль оценки безопасности исходного кода, реализующий функции поиска закладок, критичных вызовов, уязвимостей и ошибок программирования в коде на языках программирования ABAP и Java. При этом продукт учитывает специфику платформы SAP, проводит корреляцию обнаруженных уязвимостей в коде с настройками конфигурации и правами доступа и выполняет анализ лучше, чем неспециализированные продукты, работающие с теми же языками программирования.

Рисунок 9. Интерфейс Digital Security ERPScan

Из дополнительных функций ERPScan можно отметить возможность автоматической генерации исправлений для обнаруженных уязвимостей а также генерацию сигнатур для возможных атак и выгрузку этих сигнатур в системы обнаружения и предотвращений вторжений (в партнерстве с CISCO). Кроме того в системе присутствуют механизмы оценки производительности встраиваемого кода, что является критичным для бизнес-приложений, так как медленная работа дополнительных модулей может серьезно отразиться на бизнес-процессах в организации. Система также поддерживает анализ в соответствии со специфичными рекомендациями по анализу кода бизнес-приложений, такими, как EAS-SEC и BIZEC а также общими рекомендациями PCI DSS и OWASP.

Преимущества:

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

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

Solar inCode позволяет проводить анализ исходного кода, написанного на языках программирования Java, Scala, Java for Android, PHP и Objective C. В отличие от большинства конкурентов, в перечне поддерживаемых языков программирования присутствуют средства разработки для мобильных платформ Android и iOS.

Рисунок 10. Интерфейс

В случаях, когда исходный код не доступен, Solar inCode позволяет осуществить анализ готовых приложений, эта функциональность поддерживает веб-приложения и мобильные приложения. В частности, для мобильных приложений достаточно просто скопировать в сканер ссылку на приложение из Google Play или Apple Store, приложение будет автоматически загружено, декомпилировано и проверено.

Использование Solar inCode позволяет выполнить требования стандартов PCI DSS, СТО БР ИББС, а также 17 приказа ФСТЭК и требования по отсутствию недекларированных возможностей (актуально при сертификации кода).

Преимущества:

  • Поддержка анализа приложений для мобильных устройств под управлением Android и iOS;
  • поддерживает анализ веб-приложений и мобильных приложений без использования исходных текстов программ;
  • выдает результаты анализа в формате конкретных рекомендаций по устранению уязвимостей;
  • формирует детальные рекомендации по настройке средств защиты: SIEM, WAF, FW, NGFW;
  • легко интегрируется в процесс безопасной разработки ПО за счет поддержки работы с репозиториями исходных текстов.

Выводы

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

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

В данном обзоре явным лидером среди иностранных продуктов по поддержке языков программирования и качеству сканирования является решение HP Fortify Static Code Analyzer. Также хорошим продуктом является Checkmarx CxSAST, но он способен анализировать только обычные приложения и веб-приложения, поддержка встраиваемых модулей для бизнес-приложений в продукте отсутствует. Решение IBM Security AppScan Source на фоне конкурентов выглядит блекло и не отличается ни функциональностью, ни качеством проверок. Впрочем, этот продукт не предназначен для бизнес-пользователей и направлен на использование в компаниях-разработчиках, где он может показывать большую эффективность, чем конкуренты.

Среди российских продуктов сложно выделить однозначного лидера, рынок представляют три основных продукта – InfoWatch Appercut, PT Application Inspector и Solar inCode. При этом данные продукты существенно различаются технологически и предназначены для разных целевых аудиторий - первый поддерживает больше платформ бизнес-приложений и отличается большим быстродействием за счет поиска уязвимостей исключительно статическими методами анализа. Второй - сочетает в себе статический и динамический анализ, а также их комбинацию, что одновременно с улучшением качества сканирования приводит к увеличению времени проверки исходного кода. Третий же направлен на решение проблем бизнес-пользователей и специалистов по информационной безопасности, а также позволяет проверять приложения без доступа к исходному коду.

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

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


Аннотация

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

Введение

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

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

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

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

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

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

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

Процесс анализа

Процесс статического анализа состоит из двух основных шагов: создания дерева кода (также называемого ) и анализа этого дерева.

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

В общем случае дерево строится только для анализируемого фрагмента кода (например, для какой-то конкретной функции). Для того чтобы создать дерево код обрабатывается сначала , а затем .

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

Рассмотрим примерный алгоритм определения типа лексемы.

Если первый символ лексемы является цифрой, лексема считается числом, если этот символ является знаком "минус", то это - отрицательное число. Если лексема является числом, она может быть числом целым или дробным. Если в числе содержится буква E, определяющая экспоненциальное представление, или десятичная точка, число считается дробным, в противном случае - целым. Заметим, что при этом может возникнуть лексическая ошибка - если в анализируемом исходном коде содержится лексема "4xyz", лексер сочтет ее целым числом 4. Это породит синтаксическую ошибку, которую сможет выявить синтаксический анализатор. Однако подобные ошибки могут обнаруживаться и лексером.

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

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

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

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

Int Func(){return 0;}

Лексер обработает эту строку и разобьет ее на лексемы как показано в таблице 1:

Таблица 1 - Лексемы строки "int Func(){return 0};".

Строка будет распознана как 8 корректных лексем, и эти лексемы будут переданы синтаксическому анализатору.

Этот анализатор просмотрит контекст и выяснит, что данный набор лексем является объявлением функции, которая не принимает никаких параметров, возвращает целое число, и это число всегда равно 0.

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

Однако процесс построения дерева кода не сводится к простому представлению лексем в виде дерева. Рассмотрим этот процесс подробнее.

Дерево кода

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

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

При разработке вершин дерева в первую очередь обычно определяется уровень модульности. Иными словами, определяется, будут ли все конструкции языка представлены вершинами одного типа, различаемыми по значениям. В качестве примера рассмотрим представление бинарных арифметических операций. Один вариант - использовать для всех бинарных операций одинаковые вершины, одним из атрибутов которых будет тип операции, например, "+". Другой вариант - использовать для разных операций вершины различного типа. В объектно-ориентированном языке это могут быть классы вроде AddBinary, SubstractBinary, MultipleBinary, и т. п., наследуемые от абстрактного базового класса Binary.

В качестве примера разберем два выражения: 1 + 2 * 3 + 4 * 5 и 1+ 2 * (3 + 4) * 5 (см. рисунок 1).

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

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

Методы статического анализа

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

Хотя все три перечисленные выше метода статического анализа используют дерево кода, построенное синтаксическим анализатором, эти методы различаются по своим задачам и алгоритмам.

Анализ с обходом дерева, как видно из названия, выполняется путем обхода дерева кода и проведения проверок на предмет соответствия кода принятому стандарту кодирования, указанному в виде набора правил и рекомендаций. Именно этот тип анализа проводят компиляторы.

Анализ потока данных можно описать как процесс сбора информации об использовании, определении и зависимостях данных в анализируемой программе. При анализе потока данных используется граф потока команд, генерируемый на основе дерева кода. Этот граф представляет все возможные пути выполнения данной программы: вершины обозначают "прямолинейные", без каких бы то ни было переходов, фрагменты кода, а ребра - возможную передачу управления между этими фрагментами. Поскольку анализ выполняется без запуска проверяемой программы, точно определить результат ее выполнения невозможно. Иными словами, невозможно выяснить, по какому именно пути будет передаваться управление. Поэтому алгоритмы анализа потока данных аппроксимируют возможное поведение, например, рассматривая обе ветви оператора if-then-else, или выполняя с определенной точностью тело цикла while. Ограничение точности существует всегда, поскольку уравнения потока данных записываются для некоторого набора переменных, и количество этих переменных должно быть ограничено, поскольку мы рассматриваем лишь программы с конечным набором операторов. Следовательно, для количества неизвестных всегда существует некий верхний предел, дающий ограничение точности. С точки зрения графа потока команд при статическом анализе все возможные пути выполнения программы считаются действительными. Из-за этого допущения при анализе потока данных можно получать лишь приблизительные решения для ограниченного набора задач .

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

Заключение

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

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

Библиографический список

  • Dirk Giesen Philosophy and practical implementation of static analyzer tools . -Electronic data. -Dirk Giesen, cop. 1998.
  • James Alan Farrell Compiler Basics . -Electronic data. -James Alan Farrell, cop 1995. -Access mode: http://www.cs.man.ac.uk/~pjj/farrell/compmain.html
  • Joel Jones Abstract syntax tree implementation idioms . -Proceedings of the 10th Conference on Pattern Languages of Programs 2003, cop 2003.
  • Ciera Nicole Christopher Evaluating Static Analysis Frameworks .- Ciera Nicole, cop. 2006.
  • Leon Moonen A Generic Architecture for Data Flow Analysis to Support Reverse Engineering . - Proceedings of the 2nd International Workshop on the Theory and Practice of Algebraic Specifications, cop. 1997.