paint-brush
Анализ распространенных уязвимостей, создаваемых кодогенерирующим ИИк@natanjfrog
4,965 чтения
4,965 чтения

Анализ распространенных уязвимостей, создаваемых кодогенерирующим ИИ

к Natan Nehorai11m2024/02/04
Read on Terminal Reader

Слишком долго; Читать

Автоматически сгенерированному коду нельзя доверять слепо, и он по-прежнему требует проверки безопасности, чтобы избежать появления уязвимостей программного обеспечения.
featured image - Анализ распространенных уязвимостей, создаваемых кодогенерирующим ИИ
Natan Nehorai HackerNoon profile picture
0-item
1-item

Инструменты искусственного интеллекта, такие как Bard, ChatGPT и Bing Chat, в настоящее время являются громкими именами в категории «Модель большого языка» (LLM) , которая находится на подъеме.


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


На современном рынке инструментов для генерации кода ИИ присутствует множество игроков, в том числе GitHub Copilot , Amazon CodeWhisperer , Google Cloud Code (Duet AI) , Blackbox AI Code Generation и другие. В этом сообщении блога приводятся примеры распространенных ошибок безопасности, с которыми сталкиваются разработчики при использовании таких инструментов.


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


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


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


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


Многие проблемы безопасности имеют известный и определенный способ устранения без ущерба для производительности (например, можно избежать SQL-инъекций, не объединяя пользовательский ввод/параметры непосредственно в запрос) - и, следовательно, их можно устранить, поскольку нет причин писать код небезопасным способом.


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


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


Вот несколько распространенных случаев использования и примеры, которые мы протестировали, демонстрируя некоторые из этих типов уязвимостей:

  1. Вариант использования: получение файлов из пользовательского ввода — чтение произвольного файла
  2. Вариант использования: сравнение секретных токенов — жонглирование/принуждение типов
  3. Вариант использования: механизм забытого пароля — коллизии сопоставления регистров Unicode
  4. Вариант использования: создание файла конфигурации — плохие методы обеспечения безопасности
  5. Вариант использования: объекты конфигурации — несоответствия, ведущие к небезопасной десериализации


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


Вот некоторые распространенные случаи использования и примеры, которые мы протестировали, намеренно запрашивая безопасный код:

  1. Вариант использования запроса безопасного кода: чтение файлов – предотвращение обхода каталогов
  2. Запрос безопасного кода – загрузка файла – список ограниченных расширений


  1. Вариант использования: получение файлов из пользовательского ввода — чтение произвольного файла

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


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


Предложение инструмента AI (Google Cloud Code — Duet AI). Давайте попробуем создать базовую функцию извлечения файлов на основе пользовательского ввода, используя Google Cloud Code — автозаполнение Duet AI. Мы позволим Duet AI автоматически заполнять наш код на основе имен наших функций и маршрутов:



Как мы можем видеть серым цветом, предлагается использовать функцию автозаполнения Flask « send_file », которая является базовой функцией Flask для обработки файлов.


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


Правильная реализация:


Замена функции send_file безопасной функцией Flask send_from_directory снизит риск обхода каталога. Он позволит извлекать файлы только из определенного жестко запрограммированного каталога («экспорт» в данном случае).


  1. Вариант использования: сравнение секретных токенов — жонглирование/принуждение типов

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


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


Предложение инструмента AI (Amazon CodeWhisperer): наиболее распространенным примером жонглирования типами PHP являются свободные сравнения секретных токенов/хэшей, когда пользовательский ввод, считываемый с помощью запроса JSON (который позволяет контролировать тип ввода), достигает свободного сравнения (« » ) вместо строгого (« =»).


Судя по всему, CodeWhisperer генерирует свободные условия сравнения в предложениях автозаполнения:

Свободное сравнение secret_token позволяет злоумышленнику обойти сравнение $data[“secret_token”] == “secret_token”. При отправке объекта JSON со значением «secret_token», равным True (логическое значение), результат свободного сравнения также равен True (даже в более новых версиях PHP).



Даже «намекая» инструменту (с помощью комментария) написать проверку «надёжно», он не сгенерировал фрагмент кода, содержащий строгое сравнение —

Правильная реализация:

Только при указании строгого сравнения («===») мы защищены от атак подтасовки типов.


  1. Вариант использования: механизм забытого пароля — коллизии сопоставления регистров Unicode

Уязвимости в механизме «Забыли пароль» очень распространены в SaaS-приложениях и стали основной причиной таких CVE, как CVE-2017-8295 (Вордпресс), CVE-2019-19844 (Джанго) и CVE-2023-7028 (ГитЛаб).


В частности, уязвимость, связанная с столкновением регистра регистра в Юникоде, возникает, когда пользовательский ввод в выражении сравнения осуществляется в верхнем или нижнем регистре, в то время как его исходное значение также используется в коде. Некоторые разные символы при преобразовании приведут к одному и тому же коду. Например, «ß» и «SS» преобразуются в «0x00DF» в верхнем регистре.


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


Предложение инструмента AI (GitHub Copilot): механизм, который особенно восприимчив к этому типу уязвимости, — это механизм забытого пароля . Обычно при выполнении проверки вводимые пользователем данные нормализуются в верхнем или нижнем регистре (адреса электронной почты или имена доменов), чтобы избежать нежелательных несоответствий.


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


Код Copilot сначала ищет адрес электронной почты, записанный строчными буквами:

Забыли пароль – предложение ввести адрес электронной почты в запросе строчными буквами


Затем, при выполнении остальной части процесса, он предлагает следующее:

Забыли пароль – предложение использовать исходный адрес электронной почты (не в нижнем регистре) в send_email.


Как мы видим, предложение автозаполнения генерирует случайный пароль и отправляет его на адрес электронной почты пользователя. Однако сначала для получения учетной записи пользователя он использует версию адреса электронной почты пользователя в нижнем регистре, а затем продолжает использовать адрес электронной почты исходного пользователя «как есть» (без преобразования нижнего регистра) в качестве целевого адреса электронной почты для восстановления.


Например, предположим, что злоумышленник владеет почтовым ящиком «[email protected]» («K» — это символ Юникода знака Кельвина ) и использует этот адрес электронной почты для механизма «Забыли пароль». Адреса электронной почты «[email protected]» и «[email protected]» не эквивалентны «как есть», но после преобразования в нижний регистр они будут —


Использование адреса электронной почты «[email protected]» приведет к получению информации об учетной записи «[email protected]», но пароль для сброса будет отправлен на почтовый ящик злоумышленника («[email protected]»), что позволит захватить « учетная запись [email protected]».


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


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


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

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


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


В следующем примере мы создадим файл конфигурации для Kubernetes Pod , самой маленькой исполнительной единицы в Kubernetes.


Предложение инструмента AI (генерация кода AI Blackbox). В этом примере мы начинаем создавать файл конфигурации Pod.

В предложении создается раздел возможностей и добавляется возможность ядра Linux (SYS_ADMIN) в контейнер, что по сути эквивалентно запуску модуля от имени пользователя root.


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


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


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


  1. Вариант использования: объекты конфигурации — несоответствия, ведущие к небезопасной десериализации

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


В качестве примера мы выбрали библиотеку десериализации JSON компании Newtonsoft на C#, которую можно использовать безопасно или небезопасно в зависимости от конфигурации объекта JsonSerializerSettings и, в частности, TypeNameHandling .


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


Рекомендации по инструментам искусственного интеллекта (Amazon CodeWhisperer): Следующие примеры демонстрируют противоречивое поведение, основанное только на именах методов, используемых разработчиком:


Мы видим две разные конфигурации, обе из которых приводят к небезопасной десериализации JSON, поскольку свойство TypeNameHandling имеет значения «Auto» и «ALL». Эти свойства позволяют документу JSON определять десериализованный класс, что позволяет злоумышленникам десериализовать произвольные классы. Это можно легко использовать для удаленного выполнения кода, например, путем десериализации класса System.Diagnostics.Process. Единственная разница в исходном коде разработчика — это имена методов.


Правильная реализация:

По умолчанию для объекта JsonSerializerSettings создается экземпляр TypeNameHandling = TypeNameHandling.None, который считается безопасным. Таким образом, мы используем конструктор по умолчанию JsonSerializerSettings, в результате чего будет получено безопасное значение TypeNameHandling.


  1. Вариант использования запроса безопасного кода: чтение файлов — предотвращение обхода каталогов Подобно первому варианту использования, где мы исследовали уязвимый код, созданный Copilot для получения файлов, здесь мы пытаемся запросить безопасную версию механизма чтения файлов.


Предложение по инструменту AI (второй пилот GitHub):

Мы видим, что проверка безопасности действительно наивная, она позволяет избежать только последовательностей точка-точка-косая черта, которые необходимы для проведения попыток обхода каталога. Параметр пути может быть абсолютным путем, указывающим на любой желаемый путь в системе (например, /etc/passwd), что противоречит цели проверки безопасности.


Правильная реализация:

Здесь функция secure_file_read получает (относительный) параметр имени файла вместе с каталогом (safe_dir), в котором должно находиться имя файла (и которое не следует экранировать). Он создает абсолютный путь, соединяя Safe_dir и имя файла, и переходит к получению его канонизированной формы, вызывая Realpath. Затем он получает канонизированную форму папки Safe_dir. Затем содержимое файла возвращается, только если канонизированный путь начинается с канонизированного безопасного_каталога.


  1. Запрос безопасного кода — загрузка файла — список ограниченных расширений. Обычно для загрузки принимаются только определенные типы файлов в зависимости от их расширения.


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

Предлагаемый код Python берет имя файла, отделяет расширение и сравнивает его со списком опасных расширений.


На первый взгляд этот фрагмент кода выглядит безопасным. Однако соглашения об именах файлов Windows запрещают имена файлов, заканчивающиеся точкой, и при указании имени файла, заканчивающегося точкой («.») во время создания файла, точка будет отброшена. Это означает, что сгенерированный фильтр можно обойти в Windows при отправке файла с вредоносным расширением, за которым следует точка (например, «example.exe»).


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


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


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


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


Рекомендации группы исследования безопасности JFrog Чтобы защитить ваше программное обеспечение, мы рекомендуем как вручную проверять код, созданный с помощью инструментов искусственного интеллекта, так и использовать решения по обеспечению безопасности, такие как статическое тестирование безопасности приложений (SAST), которые могут надежно обнаруживать уязвимости по ходу работы. Как видно из примера выше, инструменты SAST могут предупреждать и обнаруживать конфликты сопоставления регистров Юникода, описанные в варианте использования №3. Такой упреждающий подход необходим для обеспечения безопасности вашего программного обеспечения.


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


Следите за последними открытиями и техническими обновлениями от группы исследований безопасности JFrog на нашем исследовательском веб-сайте и на X @JFrogSecurity .