Русский
Русский
English
Статистика
Реклама

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



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

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

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

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

Наиболее распространённые уязвимости в Python-коде


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

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

Командная инъекция (внедрение команд)


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

В этом примере мы используем модуль subprocess, чтобы выполнить nslookup и получить информацию о домене:

# nslookup.pyimport subprocessdomain = input("Enter the Domain: ")output = subprocess.check_output(f"nslookup {domain}, shell=True, encoding='UTF-8')print(output)

Что здесь может пойти не так?

Конечный пользователь должен ввести домен, а скрипт должен вернуть результат выполнения команды nslookup. Но, если вместе с именем домена через точку с запятой ввести ещё и команду ls, будут запущены обе команды:

$ python3 nslookup.pyEnter the Domain: stackabuse.com ; lsServer:     218.248.112.65Address:    218.248.112.65#53Non-authoritative answer:Name:  stackabuse.comAddress: 172.67.136.166Name:  stackabuse.comAddress: 104.21.62.141Name:  stackabuse.comAddress: 2606:4700:3034::ac43:88a6Name:  stackabuse.comAddress: 2606:4700:3036::6815:3e8dconfig.ymlnslookup.py

Используя эту уязвимость, можно выполнять команды на уровне ОС (у нас ведь shell = true).

Представьте себе, что случится, если злоумышленник, например, отправит на выполнение команду cat/etc/passwd, которая раскроет пароли существующих пользователей. Так что использование модуля subprocess может быть очень рискованным.

SQL-инъекция


SQL-инъекция это атака, в ходе которой из пользовательского ввода конструируется SQL-выражение, содержащее вредоносные запросы.

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

Рассмотрим пример:

from django.db import connectiondef 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 нашёл применение в двух основных сферах:

  1. DevSecOps: как один из процессов Continuous Integration (CI).
  2. Разработка: как часть локального инструментария разработчика, используется для проверки кода на уязвимость до коммита.

Как использовать 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 список идентификаторов тестов (в квадратных скобках, через запятую), которые нужно включить.

  • -s SKIPS, --skip SKIPS, где SKIPS список идентификаторов тестов (в квадратных скобках, через запятую), которые нужно исключить.

Проще всего использовать конфигурационный файл с настройками по умолчанию.

$ 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.5434 [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.5434 [0.. 50.. 100.. 150.. 200.. 250.. 300.. 350.. 400.. ][csv]  INFO  CSV output written to file: out2.csv


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

Что важнее для вас?


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



VPS серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Источник: habr.com
К списку статей
Опубликовано: 17.06.2021 16:13:58
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании маклауд

Информационная безопасность

Python

Поиск уязвимостей

Bandit

Категории

Последние комментарии

© 2006-2021, personeltest.ru