Меню Закрити

Найліпші практики з безпеки у Python

Python — одна з найбільш популярних мов програмування. Багато хто вміє програмувати на ній, але не всі знають, як програмувати так, щоб програмне забезпечення вийшло стабільним та безпечним. Спробуємо закрити цю прогалину.

Використовуйте Python 3

Незважаючи на те, що Python 3 існує вже понад десять років, чимало людей і компаній і далі використовують у роботі Python 2.7. На момент написання цієї статті Python 2.7 усе ще офіційно підтримується. Фундація Python Software оголосила, що підтримка Python 2 закінчується 1 січня 2020 року. Якщо ви ще не оновилися, ви вразливі як щодо мови, так і в межах інших проектів із відкритим кодом, які навряд чи підтримуватимуть сумісність із Python 2.7.

Наприклад, Django 1.11 — остання версія Django, сумісна з Python 2.7. Довгострокову підтримку Django 1.11 обіцяють принаймні до квітня 2020 року, але не варто покладатися на безпечну роботу з фреймворком після цієї дати.

Перехід на Python 3 не був легким для спільноти. Кардинальні зміни, введені в Python 3, означають, що розробник програмного забезпечення повинен бути впевнений, що його застарілі коди готові до оновлення, а всі залежності з відкритим кодом сумісні з Python 3.

Якщо ви досі працюєте з Python 2.7, то кращого часу для переходу вже може і не бути.

Скануйте свій код за допомогою Bandit

Простий спосіб знайти вразливості у вашому коді на Python — це сканування за допомогою Bandit.

Bandit — це проект із відкритим кодом, доступний через Python Packaging Index (PyPI). Bandit сканує кожен файл .py й створює відповідне абстрактне синтаксичне дерево (AST). Потім бандит запускає ряд плагінів, щоб знайти загальні проблеми безпеки програмного забезпечення. Наприклад, один плагін може виявити, чи використовуєте ви Flask (мікро-фреймворк для Python) із параметром налагодження, рівним True.

Bandit працює як локальний інструмент, що використовується під час розробки, або як частина вашого конвеєра CI/CD (безперервна інтеграція / безперервна доставка). Ви можете створити файл конфігурації YAML для керування поведінкою Bandit у різних сценаріях. У цьому файлі можна також зазначити список тестів, які слід пропустити. Цю функцію треба використовувати обережно.

Немає жодної гарантії, що Bandit виявить усі проблеми з безпекою: існує обмежена кількість плагінів, які він запускає, і потенційно у вашому коді може існувати проблема, що не реєструється жодним із доступних плагінів. Проте він простий у використанні та добре виявляє поширені проблеми.

Використовуйте Pipenv для керування середовищем та залежностями

Нікому не подобається, коли у продакшені відбувається щось несподіване. В ідеалі локальне середовище розробника має бути ідентичним продакшен-середовищу задля уникнення несподіванок. Тож може здатися спокусливим запустити pip freeze на вашому локальному комп’ютері, скинути отриманий список пакетів і версій у requirements.txt, а потім використовувати цей файл для налаштування продакшен-середовища. Це — легкий, але не найбезпечніший варіант.

Описаний вище процес призводить до закріплення (pinning) ваших залежностей. Коли ви закріплюєте свої залежності, то заморожуєте проект на певний час. Це добре для прогнозованості, але залишає ваш проект вразливим до вад безпеки, знайдених та усунених у сторонніх бібліотеках, які ви використовуєте.

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

Переглядайте інструкції імпорту

Імпорт Python є дуже гнучким, але гнучкість досягається ціною безпеки.

Для імпорту в Python можна використовувати абсолютний імпорт (absolute import) або відносний імпорт (relative import). Абсолютний імпорт використовує весь шлях (починаючи з кореневого каталогу) модуля, який потрібно імпортувати. Якщо модуль, який хочете імпортувати, не знайдений там, виникає помилка. Абсолютний імпорт — це хороший спосіб дізнатися, що саме ви імпортуєте.

Відносний імпорт починається зі шляху поточного модуля. Є два типи відносного імпорту — явний та неявний. Явний відносний імпорт показує точне місце розміщення модуля, який потрібно імпортувати, щодо поточного модуля. Наприклад, у вас може бути оператор імпорту такого вигляду: from .. import my_module. Точки показують, скільки каталогів треба пройти вгору.

Неявний відносний імпорт не визначає розміщення щодо поточного модуля. Якщо модуль розміщено в системному шляху, він імпортується, що може бути небезпечно. Можна створити зловмисний модуль із такою ж назвою, що й популярний модуль, а потім переправити його в популярну бібліотеку з відкритим кодом. Якщо зловмисний модуль розміщено в системному шляху, то його буде імпортовано до вашого проекту.

Оператори імпорту в Python виконують код в імпортованому модулі — це означає, що неявний відносний імпорт може призвести до виконання шкідливого коду. З цієї причини неявний відносний імпорт не підтримується в Python 3.

Якщо використовуєте Python 2, уникайте використання неявного відносного імпорту. Якщо використовуєте Python 3, треба мати на увазі, що оператори імпорту виконують код у цільовому модулі. Тому будьте обережні з операторами імпорту, незалежно від версії Python, яку використовуєте.

Будьте уважні під час завантаження пакетів

Встановити пакети Python легко. Зазвичай розробники використовують стандартний інсталятор пакетів для Python (pip), хоча Pipenv, як зазначалося вище, є чудовою альтернативою. Незалежно від того, чи використовуєте ви pip або pipenv, важливо зрозуміти, як пакети додаються до PyPI.

PyPI має процедуру звітності з проблем безпеки. Якщо хтось повідомляє про зловмисний пакет або проблему в PyPI, це розглядаєтся, але пакети, додані до PyPI, не підлягають перевірці — це було б нереалістичним очікуванням щодо добровольців, які підтримують PyPI.

Доцільно припустити, що в PyPI є шкідливі пакети, та діяти відповідно. Неодмінні заходи охоплюють невелике дослідження пакета, який хочете встановити, та ретельну перевірку написання імені пакета (пакет із відомою назвою, яка містить помилку, може виконувати шкідливий код).

Обробляйте запити безпечно

Запити HTTP Python зазвичай обробляє через бібліотеку requests. Важливо розуміти, як ця бібліотека вирішує певні проблеми безпеки, щоб ви могли бути впевнені, що отримуєте повну вигоду від неї.

Бібліотека requests здійснює перевірку сертифікатів SSL для вас. Ця бібліотека використовує пакет, який називається certifi для перевірки достовірності сертифікатів. З цієї причини дбайте про безпеку підключень, підтримуючи найновішу версію цієї бібліотеки. (Підказка: не закріплюйте залежність від версії!)

Під час здійснення запиту можна пропустити перевірку сертифіката SSL. Перевірку встановлено за замовчуванням, однак, якщо довіряєте джерелу, ви можете порушити це правило.

Синтаксис для обходу запиту на підтвердження сертифіката має такий вигляд:

requests.get(“https://snyk.io”, verify=False)

Будьте обережні під час форматування рядків

Попри те, що в Python зазвичай кожне завдання можна виконати лише в один спосіб, фактично в нього є чотири різні способи форматування рядків (три методи для версій до Python 3.6).

Форматування рядків стало значно більш гнучким і потужним (особливо цікаві f-strings), але, оскільки гнучкість збільшується, зростає й потенціал для експлойту (зловмисний код, що використовує прогалини в системі безпеки). Через це користувачі Python мають ретельно обдумувати, як вони форматують рядки з даними, введеними користувачами.

Python має вбудований модуль під назвою string. Цей модуль містить клас Template, який використовується для створення рядків шаблонів.

Розглянемо такий приклад:

from string import Template
greeting_template = Template(“Hello World, my name is $name.”)
greeting = greeting_template.substitute(name=”Hayley”)

Для зазначеного коду змінна greeting (привітання) визначається як

“Hello World, my name is Hayley.”

Цей рядковий формат є трохи громіздким, оскільки вимагає оператора імпорту та менш гнучкий із типами. Він також не визначає операторів Python так, як це роблять f-strings. При цьому, такі обмеження роблять рядки шаблонів відмінним вибором під час роботи з користувацькими даними.

Іще одна заувага щодо форматування рядків: будьте особливо обережні з вихідним SQL. Робіть свої запити за допомогою об’єктно-реляційного відображення (ORM), якщо це можливо.

Перегляньте ліцензії на залежності

Розглядаючи використання проекту з відкритим кодом, важливо зрозуміти, як ліцензуються такі проекти. Проекти з відкритим кодом є безкоштовними й доступними для використання, але в них можуть бути застосовані умови та положення. Ці умови зазвичай стосуються того, як використовується програмне забезпечення, чи потрібно робити публічно доступними зміни, які ви внесли до програмного забезпечення, та інші подібні вимоги. Ви повинні ознайомитися з потрібними для проектів ліцензіями, які ви використовуєте, щоб упевнитися, що з вашого боку немає правопорушень.

Snyk нещодавно переглянув  ліцензії на проект, доступні через PyPI. Понад 10% доступних пакетів не зазначають ліцензії. Ці пакети мають вигляд програмного забезпечення з відкритим кодом (вони доступні безкоштовно), але вони не є явно opensource.

Якщо проект має більш обмежувальну ліцензію, ніж ви очікували, це ставить вас у незручне становище. Ви повинні дотримуватися умов ліцензії або припинити використання проекту. Крім того, якщо вам потрібно внести зміни до проекту, який не має ліцензії, ви можете зіткнутися із законом про авторські права.

Щоб забезпечити стабільність вашого проекту та захистити себе, знайте, які ліцензії використовують ваші залежності та дотримуйтеся цих умов.

Десеріалізуйте вибірково

Розглянемо pickle module — частину стандартної бібліотеки Python. Pickle дає змогу вам серіалізувати й десеріалізувати структуру об’єкта Python. Якщо ви отримаєте через Pickle структуру об’єкта Python із ненадійного джерела, десеріалізація цього об’єкта може призвести до виконання шкідливого коду. Немає способу дізнатися, чи структура об’єкта, що  “розпаковується” в Pickle , є шкідливою, доки не стає запізно. Ця поведінка нещодавно була виявлена в NumPy  — популярному пакеті для наукових обчислень.

Така проблема не є характерною тільки для модуля Pickle Python. Якщо використовуєте PyYAML — парсер YAML й емітер для Python, — знайте, що він має таку ж вразливість. Якщо ви використовуєте yaml.load у файлі YAML із ненадійного джерела, він може виконувати код Python. PyYAML надає користувачам інший варіант у yaml.safe_load, який повинен бути у вас за замовчуванням.

Не десеріалізуйте дані з ненадійного джерела.

Будьте в курсі вразливостей

Що раніше ви дізнаєтеся про вразливість у ваших залежностях, то швидше усунете проблему в коді. Іноді це виправлення означає оновлення до останньої версії вашої залежності, іноді це означає виправлення, а часом означає внесення змін у ваш код, щоб гарантувати, що ви достатньо захищаєте свої вхідніі дані та уникаєте вразливих функцій.

Ви можете прочитати більше про недавній приклад цього в екосистемі Python тут.

Якщо ви знайшли помилку, будь ласка, виділіть фрагмент тексту та натисніть Ctrl+Enter.

1+

Повідомити про помилку

Текст, який буде надіслано нашим редакторам: