Module 2.6

Анализ угроз

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

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

Создание диаграмм

Что рисовать и как

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

Диаграмма потоков данных

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

Диаграмма последовательности сообщений

Что делать, если взаимодействующих сущностей больше и/или протоколы сложнее и содержат больше сообщений? Диаграмма последовательности сообщений (MSC) — это диаграмма взаимодействия, которая используется для иллюстрации компонентов коммуникационной системы и потоков обмена сообщениями между ними. В MSC у каждой сущности есть собственная вертикальная линия, а сообщения между ними изображаются горизонтальными стрелками. В MSC время идет сверху вниз.

Что включать в диаграмму

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

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

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

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

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

Первая буква в DFD означает данные, но что именно мы хотим о них знать? О данных полезно знать три вещи: откуда они пришли, куда они направляются и что они собой представляют. Во-первых, откуда пришли данные? Если данные поступили извне, они могут контролироваться злоумышленником. Во-вторых, куда данные передаются? Это помогает определить места в системе, где могут быть части, потенциально контролируемые злоумышленником. Хорошие уточняющие вопросы здесь: как данные транспортируются и где они будут храниться. Корректные права доступа к важным файлам или правильная настройка пользователей базы данных имеют большое значение. В-третьих, что это за данные? В зависимости от типа данных им может потребоваться больше защитных механизмов. Данные могут содержать информацию, которую необходимо защищать. Это могут быть номера социального страхования или другие данные, которые должны оставаться конфиденциальными, а также иные чувствительные данные, например номера кредитных карт. Обычно данные — это содержимое системы, но криптографические ключи, сертификаты и файлы конфигурации системы также следует рассматривать как данные.

DFD содержат множество блоков или иногда пузырьков (их также называют пузырьковыми диаграммами). Эти блоки выполняют обработку данных, и их нужно декомпозировать до уровня, на котором каждый блок содержит одно решение относительно данных. По сути, это означает, что нужно углубляться настолько, насколько необходимо, пока из блока не исчезнет неоднозначность. Важная информация для таких блоков — например, язык реализации. Также может быть полезно указать, откуда взялся код или блок, поскольку современные приложения повторно используют существующий код и строятся на фреймворках. По диаграмме должно быть легко понять, какой фреймворк и какая версия использовались, а также какие есть статические и динамические библиотеки. Кроме того, не следует забывать о плагинах и расширениях. Что не нужно декомпозировать? Блок обработки, который вы не разрабатываете и над которым иначе не имеете контроля. Например, браузер можно не детализировать, если система является исключительно серверным приложением. Однако поток данных к браузеру все равно нужно нарисовать.

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

Границы

DFD содержат множество прямоугольников, но что это за элементы и что в них входит? В анализе потоков данных прямоугольники — это границы, которые служат барьерами между блоками обработки. Некоторые типичные границы:

  • Границы машин
  • Изолирующие границы
  • Процессы

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

Изолирующие границы — это границы, которые не следуют из самой системы, а обычно создаются намеренно. Изолирующие границы тем или иным способом отделены от среды выполнения хоста. Например, изолирующей границей может быть изолированное окружение chroot, система мандатного контроля доступа (Mandatory Access Control, MAC), песочница или система виртуализации на уровне операционной системы.

Java VM и seccomp являются примерами песочниц. Карантинная песочница ищет вредоносные файлы в ограниченной области и определяет, представляют ли они угрозу. Такая песочница больше похожа на короткий период, в течение которого файлы сканируются, а затем выпускаются. Виртуализацию на уровне операционной системы также можно рассматривать как песочницу. Например, Docker создает иллюзию, что контейнеры являются отдельными машинами, используя chroot, пространства имен ресурсов и ограничения использования ресурсов.

AppArmor и SELinux являются примерами MAC. С их помощью можно усиливать защиту процессов, работающих в одной ОС, и обеспечивать детальные системные проверки: имеют ли процессы необходимые права доступа к ресурсам. Может возникнуть вопрос, зачем использовать такое усиление защиты в дополнение к встроенному контролю доступа ОС, но причина достаточно проста. При нормальной работе ОС удерживает приложение (его процессы) в заданных рамках, но если злоумышленник найдет способ использовать слабое место в процессе и попытается обратиться к ресурсам, которые процесс не должен читать, MAC не позволит выполнить эту операцию. Например, правила контроля доступа могут указывать, какие файлы, устройства и системные вызовы разрешены, а какие нет.

Если злоумышленник способен обойти границу, обычно безопасно предположить, что он контролирует все права доступа и все, что происходит внутри этой границы. Хотя добиться этого может быть непросто, имеет смысл исходить из худшего сценария. Кроме того, это хороший стимул поддерживать систему в актуальном и усиленном состоянии. Например, если злоумышленник каким-либо образом получит доступ к JBOSS Application Server, работающему на машине, он получит контроль над всеми приложениями, работающими на нем; если же злоумышленник получит контроль над ядром этого сервера, он получит контроль над всей машиной (включая виртуальные машины на физической машине).

Завершение

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

Протоколы являются одной из важных тем в анализе потоков данных. Типичные потоки данных используют сетевые протоколы, такие как IP, UDP, TCP, SSL, TLS, HTTP (Ethernet не упоминается, поскольку это не сквозной протокол: он завершается на каждом коммутаторе и соединяет только устройства в непосредственной близости). Эти протоколы накладываются друг на друга, и у каждого из них есть собственная задача в сетевом стеке (см. модель OSI). IP отвечает за маршрутизацию пакета от источника к цели. Следующий уровень — транспортный, он отвечает за такие задачи, как управление потоком, надежность и мультиплексирование, с использованием UDP или TCP в зависимости от требуемых характеристик трафика. Протоколы вроде TLS используются для обеспечения конфиденциальности и целостности поверх TCP (DTLS поверх UDP). Поверх этого протоколы вроде HTTP и CoAP используются для передачи запросов и ответов веб-страниц. Над ними могут находиться и протоколы прикладного уровня, которые используют все перечисленное для передачи своих протокольных сообщений.

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

Taint-анализ

Taint-анализ — это форма анализа информационных потоков, которая используется для отслеживания недоверенного ввода в системе (термин происходит из Perl). Поток — это операция или набор операций, которые работают со значением x, чтобы получить другое значение y. Если x здесь пришло из недоверенного источника, оно считается помеченным и получает тег или метку. Это делается для всех пользовательских данных. Такие теги позволяют отслеживать влияние объектов внутри целевого приложения. Пометка также распространяется транзитивно: когда помеченный объект используется для получения еще одного значения, это значение тоже становится помеченным.

Что дает такая пометка? Мы можем отслеживать пользовательские данные и проверять, могут ли недоверенные данные попасть туда, где их быть не должно, то есть в каких границах в итоге оказываются помеченные данные. Например, если недоверенные данные достигают привилегированного участка, они могут вызвать переполнение буфера, XSS и т. д. Taint-анализ полезен тем, что может обнаружить проблему даже при неизвестных атаках. При taint-анализе все операторы проверяются на наличие помеченных объектов, и если они есть, выполнение останавливается.

Фаза taint-анализа в анализе угроз проходит по всем потокам данных и всем уровням потоков и может выявить компоненты, которые обрабатывают данные. Taint-анализ — один из самых простых способов понять, какие компоненты системы наиболее подвержены воздействию данных, созданных злоумышленником. Результат taint-анализа — хороший список кандидатов для фаззинг-тестирования (рассматривалось в предыдущей части). К компонентам из этого списка также можно применить другие виды анализа.

Время жизни данных

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

Задача минимизации времени жизни фрагмента данных сложна. Можно следовать двум подходам к контролю распространения: грубозернистому и тонкозернистому. Во-первых, при грубозернистом контроле нельзя определить, когда данные, например, будут выгружены из памяти в область подкачки, а в ситуациях с дампом памяти контроль ограничен (дамп записывается в файл журнала). От выгрузки в область подкачки можно попытаться защититься, зашифровав ее с помощью dm-crypt или аналогичного средства, если это возможно, либо явно закрепив данные в памяти, что в C можно сделать, например, с помощью mmap и mlock. Если дамп памяти настроен неправильно, его содержимое может быть доступно непривилегированным пользователям, и существует риск, что дамп будет содержать чувствительную информацию. Также в ситуациях, когда дамп отправляется на другую машину, например с помощью netdump, нужно убедиться, что дамп не отправляется с переменной "NETDUMPKEYEXCHANGE", установленной в значение none. Во-вторых, тонкозернистый контроль зависит от программистов. В зависимости от языка это не всегда возможно, но, например, в C это означало бы удаление memsets. Дополнительную информацию об этом и связанных темах можно найти в руководстве по безопасному программированию для Java SE.

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

Вы дошли до конца этого раздела!

Не забудьте проверить свои баллы в индикаторе в правом нижнем углу материала!

В этой части:
  1. 1. Анализ угроз