Подписывайтесь:

Блог AST-SoftPro

Безопасность Python-приложений: OWASP Top 10 и защита

06.06.2026 15 мин чтения
Безопасность Python-приложений: OWASP Top 10 и защита

Безопасность Python-приложений: OWASP Top 10 и защита

Разработка веб-приложений на языке Python сегодня остаётся одной из самых популярных практик. Благодаря простоте синтаксиса, богатству экосистемы (включая Django, Flask, FastAPI) и высокой скорости разработки, Python активно используется в создании как простых, так и масштабируемых решений.

Однако популярность языка напрямую связана с ростом числа атак на приложения, построенные на его основе. Среди наиболее распространённых уязвимостей — те, что перечислены в OWASP Top 10: наборе критических рисков для веб-приложений, обновлённом ежегодно организацией Open Web Application Security Project (OWASP).

В этой статье рассматриваются ключевые угрозы из списка OWASP Top 2021 года, особенно актуальные для Python-проектов, и предлагаются практические меры по их предотвращению: защита от SQL-инъекций, XSS, CSRF, корректная реализация аутентификации с использованием JWT.


Уязвимости в OWASP Top 10: что важно знать разработчику на Python

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

A01:2021 – Breaking Authentication (Аутентификация без должной защиты)

Описание: Уязвимости в системе аутентификации позволяют злоумышленникам получать доступ к учётным записям, обходя или подделывая механизмы проверки личности. Это может привести к полной компрометации приложения.

Примеры уязвимых сценариев на Python:

  • Хранение паролей без хеширования (например, через user.password == 'secret123')

  • Отсутствие rate limiting при входе

  • Использование устаревших или слабых алгоритмов аутентификации

Рекомендации по защите:

  • Всегда шифровать пароли с помощью bcrypt, scrypt или Argon2 (bcrypt.hashpw()).

  • Реализовать двухфакторную аутентификацию (2FA) через TOTP.

  • Ограничить количество попыток входа за минуту (rate limiting).

from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_pwd, hashed):
    return pwd_context.verify(plain_pwd, hashed)

A02:2021 – Crypto Issues (Проблемы с криптографией)

Описание: Неправильное использование криптографии — одна из самых опасных уязвимостей. Даже простая ошибка может привести к утечке данных или возможности подделки сообщений.

Пример ошибки в Python:

import hashlib

token = hashlib.md5(payload + secret_key).hexdigest()  # Опасно! MD5 и простое сложение — не защита!

Рекомендации:

  • Не использовать встроенные криптографические функции вне специализированных библиотек.

  • Применять только проверенные алгоритмы из cryptography, hmac, os.urandom.

  • Избегать «самописной» реализации HMAC, если нет глубокого понимания протокола.

import hmac
from os import urandom

key = urandom(32)
signature = hmac.new(key, message.encode(), 'sha256').hexdigest()

A03:2021 – Injection (Инъекции)

Описание: Ввод данных пользователем используется напрямую в командных строках, SQL-запросах или других исполняемых конструкциях — это позволяет злоумышленнику выполнить произвольный код.

Подкатегории:
  • SQL-инъекция

  • Command injection

  • LDAP/NoSQL injection

A04:2021 – Insecure Design (Небезопасное проектирование)

Описание: Приложение строится без учёта принципов безопасности на этапе проектирования. Это может проявляться в избыточной открытости API, отсутствии разделения привилегий или игнорировании принципа минимальных прав.

Пример из практики Python-роутинга:

@app.route('/admin/<user_id>')
def admin_panel(user_id):
    return get_admin_data(user_id)  # Доступ к админке без проверки прав!

Рекомендации:

  • Реализовать RBAC (Role-Based Access Control).

  • Проверять права пользователя перед выполнением операций.

  • Минимизировать количество точек входа в критические функции.

A05:2021 – Security Misconfiguration

Описание: Ошибки конфигурации — одна из самых частых причин утечек. Это может быть включённый отладочный режим, незашифрованное соединение (HTTP вместо HTTPS), открытые API-ключи или ненужные расширения в зависимости.

Частые ошибки на Python:

  • Запуск Flask/FastAPI без debug=False в production.

  • Использование .env с секретами вне контроля версий (git add .env).

  • Открытие /var/log/access.log для публичного доступа.

Рекомендации по конфигурации:

# app.py — НЕПРАВИЛЬНО (в продакшене)
app.run(debug=True)

# Правильно:
if __name__ == '__main__':
    import os
    debug = not os.getenv('PRODUCTION', 'false').lower() in ('yes','true')
    print(f"Running in {'development' if debug else 'production'} mode")
    app.run(debug=debug, host='0.0.0.0', port=int(os.environ.get("PORT", 5000)))

Защита от SQL-инъекций: лучшие практики

SQL-инъекции — одна из самых распространённых уязвимостей в Python-приложениях, особенно при использовании sqlite3, psycopg2 или других прямых подключений к БД.

Проблемный код:

import sqlite3

def get_user(username):
    conn = sqlite3.connect('db.sqlite')
    cursor = conn.cursor()
    query = f"SELECT * FROM users WHERE username='{username}'"
    cursor.execute(query)
    return cursor.fetchone()  # Уязвимо к SQLi!

Решение: Использование параметризованных запросов.

def get_user(username):
    conn = sqlite3.connect('db.sqlite')
    query = "SELECT * FROM users WHERE username=?"
    cursor = conn.cursor()
    cursor.execute(query, (username,))
    return cursor.fetchone()  # Защита от SQLi!

Дополнительно:

  • Использовать ORM (SQLAlchemy, Django ORM) — они по умолчанию безопасны.

  • Применять подготовленные выражения (prepared statements) везде.

Проверка на наличие уязвимостей:

Используйте SAST-инструменты:

Инструмент Поддержка Python
Bandit ✅ Да
pycloc ❌ Нет
pip install bandit
bandit -r ./myapp/ --confidence HIGH

Защита от XSS (Cross-Site Scripting)

XSS позволяет злоумышленнику внедрять вредоносный JavaScript в страницу, которую видит жертва. Часто встречается при выводе пользовательского ввода без экранирования.

Пример уязвимости:

# Шаблон Jinja2 — НЕПРАВИЛЬНО!
<title>{{ user_input }}</title>

Если user_input = '<script>alert(1)</script>', браузер выполнит код.

Решение: Экранирование вывода.

from jinja2 import Template

template = Template("<title>{{ input | e }}</title>")  # 'e' — escape фильтр
rendered = template.render(input='<script>alert(1)</script>')
# Результат: <title>&lt;script&gt;alert(1)&lt;/script&gt;</title>

Рекомендации:

  • Всегда использовать фильтры экранирования (| e) в шаблонах.

  • Избегать innerHTML без проверки.

  • Проверять и очищать пользовательский контент (например, через bleach).

import bleach

clean_input = bleach.clean(user_input,
                           tags=[],
                           attributes={},
                           strip=True)

Защита от CSRF (Cross-Site Request Forgery)

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

Пример уязвимого кода на Flask:

@app.route('/transfer', methods=['POST'])
def transfer():
    amount = request.form['amount']
    if user.is_authenticated:
        process_transfer(amount)  # Нет проверки токена!

Решение: Использование CSRF-токенов.

Вариант 1: Flask-WTF (рекомендуется)

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField

class TransferForm(FlaskForm):
    amount = StringField('Amount')
    submit = SubmitField('Transfer')

@app.route('/transfer', methods=['GET', 'POST'])
def transfer():
    form = TransferForm()
    if form.validate_on_submit():
        process_transfer(form.amount.data)
        return redirect('/success')
    return render_template('form.html', form=form)

Вариант 2: Ручное добавление токена

import secrets

@app.before_request
def set_csrf_token():
    if request.method == 'POST' and not session.get('_csrf'):
        token = secrets.token_hex(16)
        session['_csrf'] = token

# Проверка в view:
def transfer():
    csrf = session.pop('_csrf', None)
    submitted_csrf = request.form.get('csrf')
    if csrf != submitted_csrf or not user.is_authenticated:
        return abort(403)

    process_transfer(request.form['amount'])

Аутентификация и JWT: как делать правильно

JWT (JSON Web Token) — популярный способ передачи токенов аутентификации. Однако его неправильное использование может привести к критическим уязвимостям.

A08:2021 – Broken Access Control

Описание: Возможность получить доступ к ресурсам, для которых пользователь не имеет прав.

Пример ошибки:

@app.route('/profile/<user_id>', methods=['GET'])
def get_profile(user_id):
    return user_data[int(user_id)]  # Любой может прочитать любой профиль!

Решение: Проверка владельца или роли.

from functools import wraps

def requires_role(role='USER'):
    def wrapper(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            token = request.headers.get('Authorization')
            # Парсинг и проверка JWT (упрощённо!)
            payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
            if payload.get('role') != role:
                return abort(403)
            return f(*args, **kwargs)
        return decorated
    return wrapper

@app.route('/profile/<int:user_id>')
@requires_role('ADMIN')
def get_profile(user_id):
    return user_data[user_id]

Рекомендации по JWT:

  • Никогда не передавать конфиденциальные данные в payload.

  • Использовать короткие сроки жизни (short-lived tokens).

  • Поддерживать refresh-токены отдельно от access-токенов.

  • Хранить токены только на клиенте — без localStorage, если возможно.


Дополнительные меры по безопасности Python-приложений

Помимо конкретных уязвимостей из OWASP Top 10, важно соблюдать общие практики:

Проверка зависимостей (Dependency Scanning)

Устаревшие или известные уязвимые библиотеки — частая причина инцидентов.

Инструменты:

Инструмент Описание
pip-audit Анализирует зависимости на наличие CVE
safety check Проверяет репозиторий перед пушем
# Установка безопасности зависимостей
pip install safety

# Проверка проекта
safety check --full-report > report.json

Логирование и мониторинг

Не логируйте пароли, токены или личные данные.

Пример безопасного лога:

import logging

logger = logging.getLogger('secure')
logger.info("User %d attempted login from IP %s", user.id, request.remote_addr)
# ❌ Не делать: logger.error(f"Failed login with password {password}")

Защита API

  • Валидируйте все входные данные (не только тип, но и формат).

  • Используйте rate limiting (flask-limiter или gunicorn + Redis).

  • Настройте CORS безопасно:

# flask-cors — НЕПРАВИЛЬНО:
CORS(app)

# Правильно: разрешить только нужные домены
from flask_cors import CORS

cors = CORS(app, resources={r"/api/*": {"origins": "https://trusted-app.com"}})

Заключение

Безопасность Python-приложений — не опция, а обязательное требование. Даже небольшие ошибки в коде могут привести к серьёзным последствиям: утечке данных, компрометации аккаунтов или остановке бизнеса клиента.

Ключевые шаги:

  1. Изучайте OWASP Top 10 и применяйте рекомендации для Python.

  2. Используйте безопасные практики уже на этапе проектирования (security by design).

  3. Автоматизируйте проверку кода: SAST, DAST, dependency scanning.

  4. Регулярно обновляйте зависимости и фреймворки.

  5. Проводите внутренние аудиты безопасности — даже если приложение кажется «простым».

Помните: безопасность — это процесс, а не разовое действие. Даже самая надёжная библиотека может быть скомпрометирована через ошибку в вашем коде.

AI-Помощник