pip-audit и хэши зависимостей: безопасность установки пакетов Python

Большинство приложений на Python состоит из чужого кода: десятки зависимостей, у каждой свои зависимости. Уязвимости в них находят постоянно, а установка пакета по имени без проверки позволяет подменить его (опечатка в названии, взломанный репозиторий). Две меры закрывают основную часть риска: регулярная проверка списка зависимостей на известные уязвимости утилитой pip-audit и установка с проверкой хэшей. Ниже порядок, результаты реальной проверки и скрипт для конвейера.
Статья опирается на логику направления «Управление уязвимостями» из методики зрелости Группы Сбер (источник); это методика для крупных компаний, мы используем её как ориентир и пересказываем своими словами. Проверка выполнена в временном виртуальном окружении на Debian 12 с pip-audit 2.10 и доступом к индексу пакетов и базе уязвимостей; результаты зависят от даты запуска, так как база обновляется. Проверка зависимостей в других языках и в конвейере: Dependency-Track и SonarQube в GitLab CI; общий процесс: управление уязвимостями.
Что должно получиться
| Уровень | Что это значит на практике |
|---|---|
| 2. Гигиенический | Есть процесс с приоритетами и сроками закрытия уязвимостей, включая зависимости приложений |
Критерии сформулированы нами по мотивам методики, это ориентир, а не оценка соответствия.
pip-audit: проверка на известные уязвимости
Утилита сверяет версии пакетов из вашего списка с базами уязвимостей (PyPI Advisory Database и OSV). Устанавливайте её в отдельное окружение, чтобы не смешивать с зависимостями проекта:
python3 -m venv /tmp/audit-venv
/tmp/audit-venv/bin/pip install pip-audit
/tmp/audit-venv/bin/pip-audit -r requirements.txt --no-deps --disable-pip
Ключ -r читает файл зависимостей; --no-deps --disable-pip проверяют только перечисленные точные версии без их установки и без подтягивания зависимостей зависимостей (для этого в файле должны быть закреплены все пакеты, включая транзитивные). Тестовый файл с устаревшими версиями:
requests==2.19.1
flask==0.12.2
jinja2==2.10
Результат проверки (сокращён):
Found 30 known vulnerabilities in 3 packages
Name Version ID Fix Versions
-------- ------- --------------- ------------
requests 2.19.1 PYSEC-2018-28 2.20.0
requests 2.19.1 PYSEC-2023-74 2.31.0
requests 2.19.1 PYSEC-2026-1873 2.32.0
...
Три старых пакета дали 30 записей об уязвимостях (часть записей повторяется: один и тот же дефект описан в нескольких базах). В столбце Fix Versions указаны версии, в которых проблема исправлена: это цель обновления. Для чистого списка (six==1.16.0) результат No known vulnerabilities found.
Форматы и коды возврата
| Что нужно | Как | Результат проверки |
|---|---|---|
| Машиночитаемый результат | -f json |
JSON со списком пакетов и уязвимостей: на тесте 3 пакета, 30 записей |
| Остановка конвейера при находках | код возврата утилиты | 1, если уязвимости найдены; 0, если нет |
| Исправление | --fix (при установленном окружении) |
Обновляет пакеты до исправленных версий; не применяйте без проверки совместимости |
Для конвейера используйте скрипт: он создаёт временное окружение и возвращает код 1 при находках.
#!/usr/bin/env bash
# Проверка зависимостей Python на известные уязвимости; код возврата 1, если найдены (для CI).
# Использование: ./ci-check.sh requirements.txt
set -euo pipefail
REQ=${1:?файл requirements.txt}
python3 -m venv /tmp/audit-venv
/tmp/audit-venv/bin/pip install -q pip-audit
/tmp/audit-venv/bin/pip-audit -r "$REQ" --no-deps --disable-pip
Проверка скрипта: на файле с устаревшими версиями он вывел Found 30 known vulnerabilities in 3 packages и вернул 1; на файле с six==1.16.0 вывел No known vulnerabilities found и вернул 0.
Закрепление версий и хэшей
Проверка уязвимостей — это защита от известных дефектов. От подмены пакета защищает установка с проверкой хэшей: pip сравнивает контрольную сумму скачанного файла с указанной в списке. Режим включается ключом --require-hashes и автоматически, если у хотя бы одного пакета в файле указан хэш. Получение хэша: pip hash файл.whl или утилита pip-compile --generate-hashes из набора pip-tools, которая сразу формирует закреплённый список со всеми транзитивными зависимостями.
six==1.16.0 --hash=sha256:8abb2f1d86890a2d...
Проверка установки с --require-hashes |
Результат |
|---|---|
| Верный хэш | Пакет установлен (six 1.16.0) |
| Неверный хэш (все нули) | Ошибка: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE ... someone may have tampered with them; строки Expected и Got показывают ожидаемый и реальный хэши |
| Пакет без хэша в файле | Ошибка: Hashes are required in --require-hashes mode, but they are missing from some requirements; pip выводит хэши скачанных архивов, чтобы добавить их |
Важное ограничение: хэш защищает от подмены уже зафиксированного файла, но не от того, что вы зафиксировали вредоносный пакет. Поэтому хэши добавляют после проверки источника и сверки версий, а обновления идут через проверку pip-audit и ревью изменений списка зависимостей.
Порядок внедрения
- Закрепите версии всех зависимостей, включая транзитивные (
pip freezeилиpip-compile), и храните файл в репозитории. - Включите хэши для рабочих сборок:
pip-compile --generate-hashesи установкаpip install --require-hashes -r requirements.txt. - Запускайте
pip-auditв конвейере при каждой сборке и по расписанию (раз в сутки или неделю): новые уязвимости появляются без изменения вашего кода. - Определите сроки реакции: критические уязвимости в зависимостях, доступных извне, — дни; остальные — по общему процессу: сроки закрытия.
- Оформляйте исключения: если уязвимость не применима в вашем использовании, зафиксируйте это записью с владельцем и сроком пересмотра (
--ignore-vuln ИДЕНТИФИКАТОРс комментарием в репозитории). - Следите за источниками пакетов: используйте только известный индекс (или собственное зеркало), проверяйте названия новых зависимостей на опечатки.
- Храните секреты вне кода: секреты в git.
Диагностика
| Симптом | Причина и что делать |
|---|---|
| Много находок в старом проекте | Давно не обновлялись зависимости. Сгруппируйте по пакетам, обновляйте поэтапно, начиная с доступных извне и критичных |
pip-audit предупреждает о --no-deps |
Список не полностью закреплён. Используйте pip-compile --generate-hashes |
| Ошибка сети при проверке | Нет доступа к индексу пакетов или базе уязвимостей. Проверьте прокси и разрешения для сборочного сервера |
| После закрепления хэшей установка падает на другой платформе | Хэши зависят от файла (колесо для платформы). Формируйте список для каждой целевой платформы или включайте хэши всех нужных файлов |
| Обновление пакета сломало приложение | Нет тестов или несовместимые версии. Обновляйте по одному, запускайте тесты, держите возможность отката |
Где это в модели зрелости
Материал относится к направлению «Управление уязвимостями» (материалы направления, карта зрелости). Уровень 2 требует процесса с приоритетами и сроками: проверка зависимостей приложений и защита установки от подмены закрывают отдельную группу рисков, которую сканеры инфраструктуры не видят.
Как проверить
- Все зависимости закреплены по версиям, список находится в репозитории и проверяется ревью.
pip-auditзапускается в конвейере и по расписанию, находки приводят к задачам со сроками, исключения оформлены.- Рабочие сборки используют
--require-hashes; тестовая подмена хэша приводит к ошибке сборки. - Источники пакетов известны и контролируются, новые зависимости проходят проверку.
Источники
- Методика оценки уровня зрелости кибербезопасности компании, Группа Сбер: sberbank.ru.
- Документация pip-audit: github.com/pypa/pip-audit; режим проверки хэшей в документации pip: pip.pypa.io.
- OWASP Top 10: A06 Vulnerable and Outdated Components: owasp.org. Международная практика, не российский норматив.






















