Mastodon Mastodon Mastodon Mastodon

Пошаговое руководство по написанию безопасного кода на Python: лучшие практики и примеры

Фото автора

Kamil Akbari

Опубликовано:

Обновлено:

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

Использование виртуальных окружений

Виртуальные окружения в Python позволяют изолировать зависимости проекта, снижая риски конфликтов и уязвимостей. Они обеспечивают:

  • Изоляцию зависимостей: каждое виртуальное окружение имеет свой набор зависимостей, независимых от других проектов или глобальной среды.
  • Контроль версий: вы можете контролировать версии всех используемых пакетов, обеспечивая совместимость и безопасность кода.
  • Уменьшение риска: случайная установка вредоносного пакета затрагивает только виртуальное окружение, а не всю систему.

Пример создания виртуального окружения:

python3 -m venv env
source env/bin/activate

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

Примечание редакции (2026): Начиная с Python 3.11, рекомендуется также использовать pip-audit для автоматической проверки зависимостей на известные CVE. Инструмент uv (2024+) предоставляет более быстрый альтернативный менеджер пакетов с встроенной поддержкой виртуальных окружений.

Ограничение области видимости переменных и функций

Ограничение области видимости переменных и функций снижает риск случайных изменений или нежелательного доступа к данным:

  • Избегайте глобальных переменных — они увеличивают поверхность атаки и усложняют аудит кода.
  • Используйте локальные переменные, защищённые областью видимости функции.

Пример с глобальной переменной (небезопасно):

secret = "мой супер секретный пароль"
def print_secret():
    print(secret)

print_secret()

Безопасный вариант с локальной переменной:

def print_secret():
    secret = "мой супер секретный пароль"
    print(secret)

print_secret()

Разделение кода на модули

Модульность — ключ к написанию безопасного и поддерживаемого кода. Разделяя код на независимые блоки, вы:

  • Улучшаете организацию и возможность повторного использования кода.
  • Упрощаете тестирование и отладку.
  • Уменьшаете риск внедрения уязвимостей, так как каждый модуль независим и может проверяться отдельно.

Хороший пример разделения по модулям:

# module_a.py
def do_part_one():
    print("Часть первая")

# module_b.py
def do_part_two():
    print("Часть вторая")

# main.py
from module_a import do_part_one
from module_b import do_part_two

def main():
    do_part_one()
    do_part_two()

Защита от инъекций кода

Инъекции кода входят в OWASP Top 10 как одна из наиболее критических угроз. Для защиты своего кода на Python:

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

Уязвимый код (SQL-инъекция):

def get_user(user_id):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    return execute_query(query)

Безопасный вариант с параметризованным запросом:

def get_user(user_id):
    query = "SELECT * FROM users WHERE id = ?"
    return execute_query(query, (user_id,))

Безопасная сериализация и десериализация

Сериализация и десериализация могут быть источником уязвимостей. Рекомендации:

  • Избегайте небезопасных модулей, таких как pickle, которые могут выполнять произвольный код при десериализации.
  • Используйте безопасные альтернативы — модуль json работает только с простыми типами данных.

Уязвимый код:

import pickle

def unsafe_deserialization(data):
    return pickle.loads(data)

Безопасный вариант:

import json

def safe_deserialization(data):
    return json.loads(data)

Принцип наименьших привилегий

Ограничение прав доступа программ и процессов до минимально необходимых значительно уменьшает потенциальный ущерб от уязвимостей. Этот принцип описан в руководствах NIST SP 800-53:

def process_user_data(user_data):
    # Код обрабатывает данные пользователя без ненужных привилегий
    pass

Функция process_user_data работает только с данными пользователя и не требует расширенных прав, что снижает риски в случае её компрометации.

Безопасность аутентификации и авторизации

Слабые механизмы аутентификации и авторизации могут привести к несанкционированному доступу. Пример защиты паролей с использованием хэширования:

import bcrypt

password = b"супер секретный пароль"
hashed = bcrypt.hashpw(password, bcrypt.gensalt())

Использование библиотеки bcrypt для хэширования паролей гарантирует, что даже в случае утечки данных злоумышленник не сможет легко восстановить оригинальный пароль. Для новых проектов также рассмотрите argon2-cffi — алгоритм Argon2 выиграл конкурс хэширования паролей Password Hashing Competition в 2015 году.

Правильное управление сессиями

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

  • Устанавливать флаги Secure и HttpOnly на куки сессии для защиты от перехвата и XSS-атак.
  • Регенерировать идентификатор сессии при каждом важном взаимодействии пользователя, особенно после аутентификации.
  • Устанавливать разумное время жизни сессии для снижения рисков угона сессии.

Пример конфигурации Flask:

from flask import Flask, session
from datetime import timedelta

app = Flask(__name__)
app.config.update(
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE='Lax'
)
app.permanent_session_lifetime = timedelta(minutes=15)

@app.route('/login', methods=['POST'])
def login():
    session.regenerate()
    return "Успешный вход в систему!"

Осторожность с eval() и exec()

Функции eval() и exec() позволяют выполнение произвольного кода. Их использование крайне нежелательно в продакшн-коде. Пример потенциально опасного использования:

eval('os.system("rm -rf /")')

Такой код позволит выполнить любую команду ОС с катастрофическими последствиями. Если динамическое выполнение кода необходимо, используйте ast.literal_eval() для безопасного разбора строковых литералов Python.

Статический анализ и аудит зависимостей

Автоматизированные инструменты помогают выявлять уязвимости до выхода кода в продакшн:

  • Bandit — статический анализатор безопасности Python-кода, выявляет типичные проблемы безопасности.
  • Safety / pip-audit — проверяют зависимости на наличие известных CVE из базы данных NVD.
  • Semgrep — гибкий инструмент статического анализа с правилами для Python-безопасности.

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


Kamil Akbari

Камил Акбари — автор и редактор по кибербезопасности в CyberSecureFox. Более 5 лет работает в сфере кибербезопасности, занимается разработкой software и security-инструментов. Специализируется на AI security, анализе CVE, ransomware, malware, cloud security и практиках пентестинга. При подготовке материалов опирается на official advisories, CVE/NVD, CISA, публикации вендоров и отчёты исследователей.

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.