Ищем уязвимости в Python-коде с помощью open source инструмента Bandit

Наверное, все разработчики слышали, что нужно писать чистый код. Но не менее важно писать и использовать безопасный код.
Python-разработчики обычно устанавливают модули и сторонние пакеты, чтобы не изобретать велосипеды, а использовать готовые и проверенные решения. Но проблема в том, что они не всегда тщательно проверены на уязвимости.
Часто хакеры используют эти уязвимости, в известно каких целях. Поэтому мы должны иметь возможность хотя бы фиксировать факты вторжения в наш код. А ещё лучше — заранее устранять уязвимости. Для этого нужно сначала самим найти их в коде, используя специальные инструменты.
В этом туториале мы рассмотрим, как даже в очень простом коде могут появиться уязвимости и как использовать утилиту Bandit для их поиска.
Наиболее распространённые уязвимости в Python-коде
Вы, вероятно, слышали про взломы крупных веб-сайтов и кражу данных их пользователей. Возможно, с какими-то атаками вы сталкивались лично. Через уязвимости в нашем коде злоумышленники могут получить доступ к командам операционной системы или к данным. Некоторые функции или Python-пакеты могут казаться безопасными, когда вы используете их локально. Однако при развёртывании продукта на сервере они открывают двери для хакеров.
С наиболее распространёнными атаками более-менее справляются современные фреймворки и другие умные инструменты разработки ПО, имеющие встроенную защиту. Но понятно, что не со всеми и далеко не всегда.
Командная инъекция (внедрение команд)
Командная инъекция — вид атаки, целью которой является выполнение произвольных команд в ОС сервера. Атака срабатывает, например, при запуске процесса с помощью функций модуля subprocess, когда в качестве аргументов используются значения, хранящиеся в переменных программы.
В этом примере мы используем модуль subprocess, чтобы выполнить nslookup и получить информацию о домене:
# nslookup.py import subprocess domain = input("Enter the Domain: ") output = subprocess.check_output(f"nslookup ", shell=True, encoding='UTF-8') print(output)
Что здесь может пойти не так?
Конечный пользователь должен ввести домен, а скрипт должен вернуть результат выполнения команды nslookup. Но, если вместе с именем домена через точку с запятой ввести ещё и команду ls, будут запущены обе команды:
$ python3 nslookup.py Enter the Domain: stackabuse.com ; ls Server: 218.248.112.65 Address: 218.248.112.65#53 Non-authoritative answer: Name: stackabuse.com Address: 172.67.136.166 Name: stackabuse.com Address: 104.21.62.141 Name: stackabuse.com Address: 2606:4700:3034::ac43:88a6 Name: stackabuse.com Address: 2606:4700:3036::6815:3e8d config.yml nslookup.py
Используя эту уязвимость, можно выполнять команды на уровне ОС (у нас ведь shell = true).
Представьте себе, что случится, если злоумышленник, например, отправит на выполнение команду cat для /etc/passwd, которая раскроет пароли существующих пользователей. Так что использование модуля subprocess может быть очень рискованным.
SQL-инъекция
SQL-инъекция — это атака, в ходе которой из пользовательского ввода конструируется SQL-выражение, содержащее вредоносные запросы. Благодаря активному использованию ORM количество таких атак существенно снизилось. Но если в вашей кодовой базе по-прежнему есть фрагменты, написанные на чистом SQL, необходимо знать, как строятся эти SQL-запросы. Насколько безопасны аргументы, которые вы валидируете и подставляете в запрос?
from django.db import connection def find_user(username): with connection.cursor() as cur: cur.execute(f"""select username from USERS where name = '%s'""" % username) output = cur.fetchone() return output
Тут всё просто: в качестве аргумента передадим, например, строку «Foobar». Строка вставляется в SQL-запрос, в результате чего получается:
select username from USERS where name = 'Foobar'
Так же, как и в случае с командной инъекцией, — если кто-то передаст символ «;», он сможет выполнять несколько команд. Например, добавим к нашему запросу вместо имени пользователя строку «‘; DROP TABLE USERS; —» и получим:
select username from USERS where name = ''; DROP TABLE USERS; --'
Этот запрос удалит всю таблицу USERS. Упс!
Обратите внимание, на двойной дефис в конце запроса. Это комментарий, который нейтрализует следующий за ним символ «‘». В результате команда select отработает с аргументом «»» вместо имени пользователя, а потом выполнится команда DROP, которая больше не является частью строки.
select username from USERS where name = ''; DROP TABLE USERS;
Аргументы SQL-запроса могут создать кучу проблем, если за ними не следить. Вот где инструменты для анализа безопасности могут хорошо помочь. Они позволяют найти в коде уязвимости, которые непреднамеренно внесли разработчики.
Команда assert
Не применяйте команду assert, чтобы защитить те части кодовой базы, к которым пользователи не должны получить доступ. Простой пример:
def foo(request, user): assert user.is_admin, "user does not have access" # далее идёт код с ограниченным доступом
По умолчанию __debug__ установлено в True. Однако на продакшне могут сделать ряд оптимизаций, в том числе установить для __debug__ значение False. В этом случае команды assert не сработают и злоумышленник добраться до кода с ограниченным доступом.
Используйте команду assert только для отправки сообщений о нюансах реализации другим разработчикам.
Bandit
Bandit — это инструмент с открытым исходным кодом, написанный на Python. Он помогает анализировать Python-код и находить в нём наиболее распространённые уязвимости. О некоторых из них я рассказал в предыдущем разделе. Используя менеджер пакетов pip, Bandit можно легко установить локально или на удалённую виртуалку например.
Устанавливается эта штука с помощью простой команды:
$ pip install bandit
Bandit нашёл применение в двух основных сферах:
- DevSecOps: как один из процессов Continuous Integration (CI).
- Разработка: как часть локального инструментария разработчика, используется для проверки кода на уязвимость до коммита.
Как использовать Bandit
Bandit может быть легко интегрирован как часть тестов CI, а проверки на уязвимость можно выполнять перед отправкой кода в продакшн. Например, инженеры DevSecOps могут запускать Bandit’а всякий раз, когда происходит pull-запрос или коммит кода.
Результаты проверки кода на уязвимость можно экспортировать в CSV, JSON и так далее.
Во многих компаниях существуют ограничения и запреты на использование некоторых модулей, потому что с ними связаны определённые, хорошо известные в узких кругах, уязвимости. Bandit может показать, какие модули можно использовать, а какие внесены в чёрный список: конфигурации тестов для соответствующих уязвимостей хранятся в специальном файле. Его можно создать с помощью Генератора конфигураций (bandit-config-generator):
$ bandit-config-generator -o config.yml
Сгенерированный файл config.yml содержит блоки конфигурации для всех тестов и чёрного списка. Эти данные можно удалить или отредактировать. Для указания списка идентификаторов тестов, которые должны быть включены или исключены из процедуры проверки, нужно использовать флаги -t и -s:
-
-t TESTS, —tests TESTS, где TESTS — список идентификаторов тестов (в квадратных скобках, через запятую), которые нужно включить.
$ bandit -r code/ -f csv -o out.csv [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.8.5 434 [0.. 50.. 100.. 150.. 200.. 250.. 300.. 350.. 400.. ] [csv] INFO CSV output written to file: out.csv
В команде выше после флага -r указан каталог проекта, после флага -f — формат вывода, а после флага -o указан файл, в который нужно записать результаты проверки. Bandit проверяет весь python-код внутри каталога проекта и возвращает результат в формате CSV.
После проверки мы получим достаточно много информации:


Продолжение таблицы
Как упоминалось в предыдущем разделе, импорт модуля subprocess и использование аргумента shell = True в вызове функции subprocess.check_output несут серьёзную угрозу безопасности. Если использование этого модуля и аргумента неизбежно, их можно внести в белый список в файле конфигурации и заставить Bandit’а пропускать тесты, включив в список SKIPS идентификаторы B602 (subprocess_popen_with_shell_equals_true) и B404 (import_subprocess):
$ bandit-config-generator -s [B602, B404] -o config.yml
Если повторно запустить Bandit, используя новый файл конфигурации, на выходе получим пустой CSV-файл. Это означает, что все тесты были пройдены:
> bandit -c code/config.yml -r code/ -f csv -o out2.csv [main] INFO profile include tests: None [main] INFO profile exclude tests: B404,B602 [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO using config: code/config.yml [main] INFO running on Python 3.8.5 434 [0.. 50.. 100.. 150.. 200.. 250.. 300.. 350.. 400.. ] [csv] INFO CSV output written to file: out2.csv
В условиях командной разработки для каждого проекта должны быть созданы свои файлы конфигурации. Разработчикам нужно дать возможность редактировать их в любой момент — в том числе и локально.
Что важнее для вас?
Это был короткий туториал по основам работы с Bandit. Если вы используете в своих проектах модули, в которых сомневаетесь, их можно проверить на уязвимость прямо сейчас. Да и наш собственный код мы порой не успеваем довести до ума, жертвуя не только красивыми решениями, но и о безопасностью. Каковы ваши приоритеты?
VPS серверы от Маклауд быстрые и безопасные.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!
3 инструмента для Python, которые упростят работу с кодом
Разбор полезных инструментов для Python с функциями умного ИИ автодополнения, статического и динамического анализа кода.
С опытом количество используемых в работе инструментов сокращается, поскольку многие из них вы просто перерастаете. Но такие, как эти, остаются в использовании надолго.
Инструмент 1: ИИ автодополнение и быстрый доступ к документации с Kite
У современных IDE есть встроенное автозаполнение, которое выглядит примерно так:
Прим.ред. Такая функциональность доступна в планах Pro и Team. На момент написания материала для плана Pro действует бесплатная бета-версия.
Разберём подробнее особенности Kite — инструмента для Python с функциями умного автодополнения и быстрого доступа к документации.
Умные подсказки
Плагин Kite смотрит в комплексе ваш код, переменные, часто используемые имена параметров, документацию, и только после сбора всех данных рекомендует что-то вроде этого:
На данный момент этот блок не поддерживается, но мы не забыли о нём! Наша команда уже занята его разработкой, он будет доступен в ближайшее время.
Copilot для документации
Прежде чем беспокоить более опытного коллегу или мчаться за ответами на Stack Overflow, почитайте документацию.
Kite Copilot упрощает поиск по документации. Он работает параллельно с IDE и показывает информацию о любых объектах или функциях, на которые наведён курсор.
Работает локально, приватно
Вдобавок ко всему, плагин создан для локальной работы, так что вы получаете быстрые советы, работа происходит в автономном режиме, и ваш код никогда не будет отправлен в облако.
Это крайне важно для людей с плохим интернет-соединением и тех, кто работает с закрытым исходным кодом.
Всё, что нужно сделать, это загрузить и установить плагин Kite для вашего редактора: есть бесплатная версия и Free Beta на Pro план с умным автозаполнением.
Инструмент 2: Статический анализ кода с Mypy
Python — динамически типизированный язык. Это значит, что переменная связывается с типом данных не в момент объявления, а в момент присваивания ей значения. То есть одна и та же переменная может быть и строкой, и целым числом, и каким-либо другим типом в зависимости от последнего присвоенного ей значения.
# Например, вот одна и та же переменная с разными типами данных. # Python определяет тип данных динамически: # string var_name = "string here" # integer var_name = 1234
А вот примеры языков со статической типизацией, где для каждой переменной задан один конкретный тип данных, и в логике кода нужно придерживаться именно его:
# Для многих языков обязательно предварительное объявление типа. # string str str_var_name = "string here" # integer int int_var_name = 1234
Плюсы и минусы динамической типизации
Главное преимущество динамической типизации в том, что вам может быть лень постоянно прописывать типы, и такой язык, как Python, в этом поможет.
А вот недостатков больше:
- как правило, вы сталкиваетесь с ошибками на более поздних этапах разработки;
- код работает медленнее, ведь Python постоянно вычисляет типы;
- код становится менее безопасным, так как на входе и выходе функции у одной и той же переменной могут быть разные типы данных;
- читать ваш код становится сложнее, поскольку другой человек не может быть уверен в том, что уже объявленная переменная не изменит свой тип в дальнейшем.
Статическая типизация в Python
В Python нет статической типизации как таковой, но есть аннотации типов, которые проверяются статическими анализаторами с целью контроля типов переменных.
Обратите внимание на Mypy. Это статический анализатор типов для Python, который позволяет находить ошибки несоответствия типов в коде. Mypy выводит ошибку, если при работе с аннотациями типов значение переменной не соответствует присвоенному ей типу.
from typing import Iterator def fib(n: int) -> Iterator[int]: a, b = 0, 1 while a < n: yield a a, b = b, a + b fib(10) fib("10")
В результате запуска статического анализатора Mypy в приведённом выше коде вы получите следующую ошибку: main.py:10: error: Argument 1 to "fib" has incompatible type "str"; expected "int" . Таким образом Mypy предупреждает, что мы пытаемся присвоить строковое значение переменной с целочисленным типом данных. Это лишь один из примеров использования Mypy. Все функциональные возможности инструмента, более подробно описанные в документации, можно протестировать в песочнице.
Если ваш код работает на проде, и вам важна его устойчивость к ошибкам, связанным с типизацией, используйте инструмент Mypy.
Инструмент 3: Быстрый поиск ошибок и чистый код с SonarLint
Сейчас почти во всех IDE есть линтер — статический анализатор возможных ошибок. Другими словами, он предугадывает ещё до запуска кода, что может пойти не так, и выделяет предполагаемые ошибки.
В свою очередь, динамический анализ не предугадывает, а действительно запускает/компилирует части кода, чтобы определить, работает ли он, но делает это автоматически в фоновом режиме. То есть он на самом деле знает, что пойдёт не так в процессе выполнения программы.
SonarLint — это тот самый динамический анализатор кода, который поможет решить следующие проблемы.
Лишний код
Допустим, вы не удалили закомментированные части кода, оставили неиспользуемые функции и прочие рудименты. SonarLint предупредит об этом, и вы сможете своевременно очистить код от всего лишнего.
Уязвимости
Большая обновляемая база данных с перечнем уязвимостей позволяет плагину вовремя предупреждать о любых известных уязвимостях, которые встречаются в вашем коде.
Когнитивная сложность
Подробнее об этом можно прочесть в статье. Если говорить кратко, разработчики плагина создали математическую формулу, которая может оценить, насколько читабелен ваш код.
Это не только полезно, но и просто. Каждый раз, когда SonarLint предупреждает вас о чрезмерной сложности, это сопровождается объяснением правила, которое вы нарушили. Например, «слишком большая вложенность операторов if», более известная как спагетти-код. Удобно, правда?
Плагин SonarLint позволяет использовать лучшие практики и писать понятный чистый код.
Подытожим
- ИИ автодополнение и быстрый доступ к документации с Kite Copilot и плагином для IDE.
- Статическая типизация кода с модулем Mypy.
- Быстрый поиск ошибок и чистый код с плагином SonarLint.
Все перечисленные инструменты для Python бесплатны или же поставляются в нескольких вариантах, включающих бесплатный.
Python проверка кода на безопасность
Python не первый год входит в Топ-4 языков программирования по популярности. Его используют для различных целей:
- При автоматизации задач в системном администрировании.
- В банковской сфере. Например, один из крупнейших банков России применяет его для управления своими банкоматами.
- Во встроенных системах станков с ЧПУ.
- В телекоммуникационном оборудовании.
- При разработке мобильных приложений (преимущественно для серверной части).
- В Data Science, при разработке облачных решений, в области машинного обучения, искусственного интеллекта.
Учитывая важность программного обеспечения и информационных систем, в которых применяется этот язык программирования, можно судить о том, насколько живой интерес проявляют к ним злоумышленники. Так что проверке безопасности Python и созданных с его помощью продуктов необходимо уделять должное внимание.
Методы и инструменты тестирования уязвимости Python
При анализе безопасности программ, написанных на этом языке, используются те же подходы и технологии, как и в случае с другими. Для тестирования безопасности программного обеспечения применяются методы:
- DAST – динамическое тестирование методом черного ящика. Выявляет проблемы в работающем приложении – воздействуя на интерфейсы программы и анализируя ее реакцию на такие события. Для анализа уязвимости написанных на Python продуктов с помощью этого метода требуются специалисты в области тестирования на проникновение. Также необходимо развертывание отдельных сред для тестирования, что могут позволить себе далеко не все конечные пользователи программного обеспечения.
- SAST – тестирование безопасности Python методом белого ящика. Для SAST-анализатора исходного кода не требуется запуск приложения. Кроме того, результаты работы этого инструмента представляются в удобном для восприятия виде: с ними может работать специалист, не имеющий опыта в разработке (офицер по информационной безопасности, системный администратор). Более того, SAST-инструменты могут использовать и конечные пользователи программ. Еще одно преимущество этот метода при тестировании уязвимости – проверка не только самой программы, но и сторонних компонентов и библиотек, что для этого языка программирования с присущим ему огромным количеством зависимостей весьма актуально.
- IAST – интерактивное тестирование безопасности. В методе сочетаются черты двух предыдущих. за счет чего покрывается большее количество кода, выдаются более точные результаты, а также достигается ряд других преимуществ. Но чтобы использовать его, потребуется собственная команда разработчиков для интерпретации некоторых результатов, а также развертывание отдельных сред для тестирования.
SAST-тестирование безопасности Python
При разработке анализатора исходного кода Solar appScreener мы опирались на метод тестирования безопасности белого ящика, который могут использовать как разработчики программного обеспечения, так и пользователи.
Тестирование уязвимости с помощью SAST-инструментов позволяет выявлять как «универсальные», характерные для многих языков программирования уязвимости (например, места в коде, через которые возможны атаки с помощью инъекций или парсинг XML), так и специфичные. SAST-анализатор исходного кода находит конструкции, через которые может быть реализована угроза, и формирует оповещения об этом.
При проверке уязвимостей методом белого ящика выявляются слабые места в работе с вводимыми пользователями и принимаемыми от сторонних источников данными. SAST находит проблемы с авторизацией, работой с базами данных, сессиями и так далее. За счет покрытия почти 100% кода практически ни одна уязвимость не остается незамеченной.
Проверка безопасности Python с помощью SAST-анализаторов позволяет находить практически все типовые проблемы, характерные для этого языка. Среди них:
- Уязвимости, связанные с использованием в процессе разработки старых версий языка и небезопасных конструкций, из-за чего существует вероятность реализации некоторых угроз. Пример – функция init() в Python 2.7, которая работает аналогично небезопасной eval в более новых версиях языка. Через нее возможно выполнение сторонних скриптов (которые могут быть вредоносными). Пример небезопасной конструкции – использование assert для защиты фрагментов кода, к которым не должны обращаться пользователи.
- Публичное отображение ошибок. Это может отрицательно повлиять на безопасность приложения. SAST-инструмент может находить такие моменты (пример – значение true для debug при использовании Django), опираясь на стандартные или добавляемые пользователями правила.
- Пакеты с вредоносным кодом в модулях и уязвимости, проблемы, связанные с импортированием. При разработке программного обеспечения на этом языке программирования может использоваться множество библиотек, устанавливающихся с помощью Pip. Инструменты статического анализа кода проверяют не только само приложение, но и подключаемые компоненты. Еще одна потенциальная угроза для программ на Python – использование относительных и неявных путей для импорта. Такие проблемы выявляются с помощью стандартных правил SAST-инструментов, а также правил, задаваемых пользователями.
Это лишь малая часть проблем, которые можно выявить в программах на Python при анализе исходного кода методом белого ящика. Возможности современных SAST-анализаторов позволяют находить довольно сложные уязвимости, на поиск которых другими методами ушло бы немало времени. И еще один весомый плюс таких инструментов – возможность применения SAST-анализа на всех этапах жизненного цикла программного обеспечения, включая стадию его эксплуатации конечными пользователями.
Проверка уязвимостей в коде Python с помощью Bandit

В этом руководстве мы рассмотрим, как простые строки кода могут оказаться разрушительными, и как с помощью Bandit можно их выявить.
Введение
Как разработчиков, нас с самого начала пути призывают писать чистый код. Не менее важным, но менее обсуждаемым является написание и использование безопасного кода.
В проектах Python мы обычно устанавливаем модули и пакеты сторонних разработчиков, чтобы избежать разработки уже существующих решений. Однако из-за этой распространенной практики хакеры используют зависимости, чтобы посеять хаос в нашем программном обеспечении, и поэтому нам нужно уметь обнаруживать, когда происходит что-то не так. Для этого мы используем такие инструменты, как Bandit, утилита анализа безопасности с открытым исходным кодом для проектов на Python.
Уязвимости в Python
Уязвимость безопасности в нашем коде — это недостаток, которым могут воспользоваться злоумышленники для эксплуатации наших систем и/или данных. Когда вы программируете на Python, вы можете обнаружить некоторые уязвимости в использовании функциональных вызовов или импорта модулей, которые могут быть безопасными при локальном вызове, но могут открыть двери для злоумышленников для вмешательства в систему при развертывании без правильной конфигурации.
Вы, вероятно, сталкивались с несколькими из них в своей повседневной деятельности по написанию кода. С некоторыми из наиболее распространенных атак и эксплойтов в значительной степени справляются современные фреймворки и системы, которые предвосхищают такие атаки.
Вот некоторые из них:
Уязвимость OS Command Injection
Основана на модуле subprocess, который используется для выполнения утилит командной строки и вызова процессов, связанных с операционной системой. Следующий фрагмент использует модуль подпроцесса для выполнения поиска DNS и возвращает результат:
import subprocess input_domain = input("Введите домен: ") result = subprocess.check_output( f"nslookup ", shell=True, encoding="UTF-8" ) print(result)
Что здесь может пойти не так?
В идеальном сценарии конечный пользователь предоставляет имя домена, и сценарий возвращает результаты команды nslookup. Но если вместе с доменом они предоставят команду на базе ОС, например ls, то будет получен следующий результат — команда тоже будет выполнена:
python3 command_inject.py Введите домен: egorovegor.ru ; ls Server: 192.168.13.1 Address: 192.168.13.1#53 Non-authoritative answer: Name: egorovegor.ru Address: 92.53.116.135 command_inject.py
Позволив кому-то передать часть команды, мы дали ему доступ к терминалу на уровне ОС.
Представьте себе, насколько разрушительными могут быть последствия, если злоумышленник передаст такую команду, как cat /etc/passwd, которая раскроет пароли существующих пользователей. Как бы просто это ни звучало, модуль subproccess может быть очень рискованным в использовании.
Уязвимость SQL Injection
Атаки SQL Injection в наши дни редки благодаря широко используемым функциям ORM. Но если вы все еще придерживаетесь использования необработанного SQL, вам необходимо знать, как строятся ваши SQL-запросы и насколько безопасно проверяются и передаются параметры запроса.
Рассмотрим следующий фрагмент кода:
from django.db import connection def find_user(login): '''Поиск пользователя в базе данных''' with connection.cursor() as cursor_db: cursor_db.execute(f"""select login from USERS where name = ''""") result = cursor_db.fetchone() return result
Вызов функции прост — вы передаете в качестве аргумента строку, скажем, «Ivan», и эта строка вставляется в SQL-запрос, в результате чего получается:
select login from USERS where name = 'Ivan'
Однако, как и в предыдущем случае, если кто-то добавит символ ;, то сможет создать цепочку из нескольких команд. Например, вставка ‘; DROP TABLE USERS; — приведет к следующему:
select login from USERS where name = ''; DROP TABLE USERS; --'
Первый оператор будет запущен прямо перед тем, как база данных сбросит всю таблицу USERS. Вот это да!
Обратите внимание, что последняя кавычка была закомментирована с помощью двойного тире. Параметры SQL-запросов могут стать кошмаром, если их не проанализировать должным образом. Вот где инструменты безопасности могут помочь в обнаружении таких непреднамеренных, но вредных строк кода.
Библиотека Bandit
Bandit — это инструмент с открытым исходным кодом, написанный на языке Python, который поможет вам проанализировать ваш Python-код и найти в нем общие проблемы безопасности. Он сможет просканировать ваш Python-код, найти уязвимости и эксплойты, такие как те, что были упомянуты в предыдущем разделе.
Установка
Bandit можно установить локально или в виртуальной среде с помощью pip:
pip install bandit
Использование
Bandit можно использовать в следующих кейсах:
- DevSecOps: включение Bandit как части практики непрерывной интеграции (CI).
- Разработка: Bandit можно использовать локально как часть локальной настройки разработки, где разработчики могут контролировать эксплуатацию функций до фиксации кода.
Bandit может быть легко интегрирован в CI-тесты, и перед отправкой кода в производство можно проводить общие проверки на уязвимости.
Например, инженеры DevSecOps могут вызывать Bandit всякий раз, когда поднимается запрос на перенос или происходит фиксация кода, для повышения безопасности. На основе рекомендаций организации можно разрешить или ограничить импорт модулей и вызовы функций.
Bandit предоставляет пользователям контроль над тем, какие модули использовать, а какие занести в черный список. Этот контроль определяется в конфигурационном файле, который может быть создан с помощью инструмента bandit-config-generator. Результаты выполняемых тестов кода могут быть экспортированы в виде CSV, JSON и т.д.
Конфигурационный файл может быть сгенерирован вот так:
bandit-config-generator -o config.yml
Созданный файл config.yml содержит несколько частей, соответствующих тестам, которые могут быть разрешены или отменены, вызовам функций, которые могут быть разрешены или отменены, а также максимальной длине криптографических ключей. Пользователь может использовать bandit, указав этот конфигурационный файл или выполнить все тесты, просто передав директорию проекта:
bandit -r code/ -f csv -o out.csv [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.8.5 434 [0.. 50.. 100.. 150.. 200.. 250.. 300.. 350.. 400.. ] [csv] INFO CSV output written to file: out.csv
В этом вызове Bandit вы будете указывать каталог проекта с помощью флага -r и записывать вывод в виде CSV с помощью флага -o. Bandit тестирует все скрипты Python внутри этого каталога проекта и возвращает вывод в виде CSV. Вывод очень подробный, и вот как он выглядит:
| filename | test_name | test_id | issue_severity | issue_confidence | issue_text | line_number | line_range | more_info |
| command_inject.py | blacklist | B404 | LOW | HIGH | Consider possible security implications associated with subprocess module. | 1 | [1, 2] | https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess |
| command_inject.py | subprocess_popen_with_shell_equals_true | B602 | HIGH | HIGH | subprocess call with shell=True identified, security issue. | 5 | [4, 5] | https://bandit.readthedocs.io/en/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html |
| sql_inject.py | hardcoded_sql_expressions | B608 | MEDIUM | MEDIUM | Possible SQL injection vector through string-based query construction. | 7 | [7] | https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html |
Как уже упоминалось в предыдущем разделе, импорт модуля subprocess и аргумент shell=True представляют высокую угрозу безопасности. Если использование этого модуля и аргумента неизбежно, их можно внести в белый список конфигурационного файла и заставить его пропустить тесты, включив коды B602 (subprocess_popen_with_shell_equals_true) и B404 (import_subprocess) в «skips». Вы можете найти эти коды в сгенерированном конфигурационном файле. Тесты, включенные в файл в секции skips, следующие:
skips: [B602, B404]
Если вы повторно запустите тесты Bandit, используя сгенерированный файл конфигурации, это приведет к созданию пустого CSV-файла, обозначающего, что все тесты были пройдены:
bandit -c config.yml -r code/ -f csv -o out2.csv [main] INFO profile include tests: None [main] INFO profile exclude tests: B404,B602 [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO using config: code/config.yml [main] INFO running on Python 3.8.5 434 [0.. 50.. 100.. 150.. 200.. 250.. 300.. 350.. 400.. ] [csv] INFO CSV output written to file: out2.csv
Для совместной работы внутри организации этот файл конфигурации bandit должен быть встроен во вновь создаваемые проекты, чтобы разработчики могли иметь к нему доступ даже локально.
Заключение
Код должен быть чистым и безопасным. В этом кратком руководстве мы рассмотрели Bandit, библиотеку Python, используемую для выявления типичных проблем безопасности в модулях, которые вы, вероятно, уже используете.