В веб-приложении на Flask встречается код: user = request.args.get('user'); query = "SELECT * FROM users WHERE name = '" + user + "'"; results = db.execute(query) — выявите уязвимости (SQL-инъекции, XSS, CSRF и т.п.), предложите конкретные исправления и обсудите подходы к хранению паролей и управлению сессиями
Код user = request.args.get('user') query = "SELECT * FROM users WHERE name = '" + user + "'" results = db.execute(query)
содержит несколько серьёзных проблем безопасности. Ниже — что именно уязвимо, почему, и конкретные исправления + рекомендации по хранению паролей и управлению сессиями.
1) Уязвимости в исходном коде
SQL‑инъекция. Формирование SQL через конкатенацию строки с участием пользовательского ввода даёт возможность выполнить произвольный SQL (например: user = "x' OR '1'='1";).XSS (reflected/stored). Если значение user затем выводится в HTML без экранирования — возможна межсайтовая скриптовая атака.CSRF (если этот GET/POST используется для изменения состояния без защиты). Хотя пример показывает только SELECT — общая практика использования небезопасных эндпоинтов и отсутствие CSRF‑защиты рискована.Утечки информации / логирование. Если логируете raw-запросы, можно слить секреты.Неправильное хранение паролей (если пароли вообще хранятся в виде plain text в users) — очень опасно.Неправильная конфигурация сессий (незащищённые cookies, отсутствие HttpOnly/Secure/SameSite).
2) Конкретные исправления (по порядку) A. Защита от SQL‑инъекций — использовать параметризованные запросы / ORM
Psycopg2 / psycopg2‑binary (Postgres): cur.execute("SELECT * FROM users WHERE name = %s", (user,))sqlite3: cur.execute("SELECT * FROM users WHERE name = ?", (user,))SQLAlchemy (рекомендуется в Flask): ORM
from sqlalchemy import text results = db.session.execute(text("SELECT * FROM users WHERE name = :n"), {"n": user})
Важно: никогда не вставляйте user напрямую в строку SQL.
B. Экранирование и защита от XSS
При рендере HTML используйте шаблонизатор с авто‑эскейпингом (Jinja2 в Flask эскейпит по умолчанию). Пример: return render_template("profile.html", user=user)Если вам нужно вывести HTML, убедитесь, что вы доверяете содержимому (и используйте Markup/escape осознанно).Дополнительно — заголовки CSP (Content‑Security‑Policy) и правильный набор заголовков безопасности (X-Frame-Options, X-Content-Type-Options).
C. CSRF
Для форм/POST/изменяющих запросов используйте CSRF‑токены (Flask-WTF или flask_wtf.csrf.CSRFProtect). from flask_wtf.csrf import CSRFProtect csrf = CSRFProtect(app)Для API обычно используют токены в заголовке авторизации (Bearer) и CORS/SameSite политики вместо cookie‑CSRF.
D. Валидация и нормализация ввода
По возможности применяйте allow‑list: длина, допустимые символы (например, имена пользователей — только латиница/цифры/символы).Для поиска по LIKE используйте параметризованные запросы и экранирование wildcard‑символов.
3) Пример безопасного кода (Flask + SQLAlchemy + CSRF) Пример минимальной реализации получения пользователя: from flask import Flask, request, render_template from flask_wtf.csrf import CSRFProtect from models import User # SQLAlchemy модель
app = Flask(name) csrf = CSRFProtect(app)
@app.route('/user') def user_profile(): name = request.args.get('user', '', type=str)
Валидация: ограничить длину и набор символовif not name or len(name) > 100: abort(400) user = User.query.filter_by(name=name).first() # параметризовано return render_template('profile.html', user=user) # Jinja2 эскейпит автоматически
Argon2 (рекомендуется), bcrypt, scrypt. PBKDF2 возможно, но Argon2 предпочтительнее на 2024+.
Используйте проверенные библиотеки:
argon2-cffi: from argon2 import PasswordHasherpasslib (оборачивает bcrypt/argon2 и даёт удобство)werkzeug.security.generate_password_hash (использует PBKDF2 по умолчанию; приемлемо, но Argon2/Bcrypt лучше)
Пример (argon2): from argon2 import PasswordHasher ph = PasswordHasher(time_cost=2, memory_cost=65536, parallelism=4) # настраивайте под вашу infra hash = ph.hash(plain_password)
Используйте уникальную соль для каждой записи (библиотеки сами генерируют соль).Храните только hash (и параметры, если библиотека не включает их).Используйте constant-time сравнение при проверке (библиотеки делают это).Планируйте миграцию параметров (увеличение cost) — храните метаданные для каждого хеша или используйте passlib, который умеет ре-хешировать при входе.
5) Управление сессиями
Конфигурация cookie: SESSION_COOKIE_SECURE = True (только HTTPS)SESSION_COOKIE_HTTPONLY = True (не доступно JS)SESSION_COOKIE_SAMESITE = 'Lax' или 'Strict' (предпочтительно Lax для большинства)Flask по умолчанию хранит сессии в client‑signed cookies. Для большей безопасности храните сессии на сервере (Redis/DB) через Flask-Session: from flask_session import Session app.config['SESSION_TYPE'] = 'redis' Session(app)Защита от фиксации сессий: Генерируйте новый идентификатор сессии при логине (session.clear() и заполнение новой сессии).На логаут обязательно удаляйте сессию на сервере и cookie.Ограничение времени жизни: app.permanent_session_lifetime = timedelta(hours=1)Используйте короткие тайм‑ауты для чувствительных операций и опцию "запомнить меня" с долгосрочной токенизацией, а не долговременными сессионными куки.Аутентификация: Используйте Flask-Login для управления логином/логаутом, и храните только идентификатор пользователя в сессии.Для API — используйте JWT аккуратно (реплэй/отзыв проблемны) или токены доступа + refresh токены, хранимые в httpOnly cookie и с проверкой на сервере.
6) Дополнительные рекомендации
HTTPS всегда.Ограничение частоты (rate limiting) и защита от брутфорса (Flask-Limiter, блокировки, задержки).Логирование не содержит секретов (не логируйте пароли, токены, полные запросы с секр. параметрами).Мониторинг, IDS/IPS и тестирование (DAST, SAST, регулярные pentest).Минимизация прав БД: приложение использует пользователя БД с минимальными правами (SELECT/INSERT/UPDATE только по необходимости).Принцип наименьших привилегий для секретов и сервисов.
Кратко — что нужно сделать прямо сейчас
Заменить конкатенацию SQL на параметризованные запросы / ORM.Экранировать/валидировать пользовательский ввод и эскейпить вывод в шаблонах (Jinja2 по умолчанию).Подключить CSRF‑защиту для форм/изменяющих запросов.Перейти на безопасное хранение паролей (Argon2/bcrypt через библиотеки).Пересмотреть конфигурацию сессий (Secure, HttpOnly, SameSite), и при необходимости перейти на серверные сессии + регенерация сессии при логине.
Если хотите, могу:
Привести готовый пример приложения Flask с SQLAlchemy, Argon2 и Flask‑Login, где все эти рекомендации реализованы,Или показать конкретные примеры под вашу СУБД (Postgres/SQLite/MySQL).
Код
user = request.args.get('user')
query = "SELECT * FROM users WHERE name = '" + user + "'"
results = db.execute(query)
содержит несколько серьёзных проблем безопасности. Ниже — что именно уязвимо, почему, и конкретные исправления + рекомендации по хранению паролей и управлению сессиями.
1) Уязвимости в исходном коде
SQL‑инъекция. Формирование SQL через конкатенацию строки с участием пользовательского ввода даёт возможность выполнить произвольный SQL (например: user = "x' OR '1'='1";).XSS (reflected/stored). Если значение user затем выводится в HTML без экранирования — возможна межсайтовая скриптовая атака.CSRF (если этот GET/POST используется для изменения состояния без защиты). Хотя пример показывает только SELECT — общая практика использования небезопасных эндпоинтов и отсутствие CSRF‑защиты рискована.Утечки информации / логирование. Если логируете raw-запросы, можно слить секреты.Неправильное хранение паролей (если пароли вообще хранятся в виде plain text в users) — очень опасно.Неправильная конфигурация сессий (незащищённые cookies, отсутствие HttpOnly/Secure/SameSite).2) Конкретные исправления (по порядку)
Psycopg2 / psycopg2‑binary (Postgres):A. Защита от SQL‑инъекций — использовать параметризованные запросы / ORM
cur.execute("SELECT * FROM users WHERE name = %s", (user,))sqlite3:
cur.execute("SELECT * FROM users WHERE name = ?", (user,))SQLAlchemy (рекомендуется в Flask):
ORM
user_obj = User.query.filter_by(name=user).first()
или text + параметрыfrom sqlalchemy import text
results = db.session.execute(text("SELECT * FROM users WHERE name = :n"), {"n": user})
Важно: никогда не вставляйте user напрямую в строку SQL.
B. Экранирование и защита от XSS
При рендере HTML используйте шаблонизатор с авто‑эскейпингом (Jinja2 в Flask эскейпит по умолчанию). Пример:return render_template("profile.html", user=user)Если вам нужно вывести HTML, убедитесь, что вы доверяете содержимому (и используйте Markup/escape осознанно).Дополнительно — заголовки CSP (Content‑Security‑Policy) и правильный набор заголовков безопасности (X-Frame-Options, X-Content-Type-Options).
C. CSRF
Для форм/POST/изменяющих запросов используйте CSRF‑токены (Flask-WTF или flask_wtf.csrf.CSRFProtect).from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)Для API обычно используют токены в заголовке авторизации (Bearer) и CORS/SameSite политики вместо cookie‑CSRF.
D. Валидация и нормализация ввода
По возможности применяйте allow‑list: длина, допустимые символы (например, имена пользователей — только латиница/цифры/символы).Для поиска по LIKE используйте параметризованные запросы и экранирование wildcard‑символов.3) Пример безопасного кода (Flask + SQLAlchemy + CSRF)
Пример минимальной реализации получения пользователя:
from flask import Flask, request, render_template
from flask_wtf.csrf import CSRFProtect
from models import User # SQLAlchemy модель
app = Flask(name)
csrf = CSRFProtect(app)
@app.route('/user')
Валидация: ограничить длину и набор символовif not name or len(name) > 100:def user_profile():
name = request.args.get('user', '', type=str)
abort(400)
user = User.query.filter_by(name=name).first() # параметризовано
return render_template('profile.html', user=user) # Jinja2 эскейпит автоматически
4) Хранение паролей — лучшие практики
Никогда не храните пароли в открытом виде.
Используйте адаптивные KDF (key derivation function):
Argon2 (рекомендуется), bcrypt, scrypt. PBKDF2 возможно, но Argon2 предпочтительнее на 2024+.Используйте проверенные библиотеки:
argon2-cffi: from argon2 import PasswordHasherpasslib (оборачивает bcrypt/argon2 и даёт удобство)werkzeug.security.generate_password_hash (использует PBKDF2 по умолчанию; приемлемо, но Argon2/Bcrypt лучше)Пример (argon2):
проверка:from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=2, memory_cost=65536, parallelism=4) # настраивайте под вашу infra
hash = ph.hash(plain_password)
try:
неверный парольph.verify(hash, provided_password)
except VerifyMismatchError:
Важные замечания:
Используйте уникальную соль для каждой записи (библиотеки сами генерируют соль).Храните только hash (и параметры, если библиотека не включает их).Используйте constant-time сравнение при проверке (библиотеки делают это).Планируйте миграцию параметров (увеличение cost) — храните метаданные для каждого хеша или используйте passlib, который умеет ре-хешировать при входе.5) Управление сессиями
Конфигурация cookie:SESSION_COOKIE_SECURE = True (только HTTPS)SESSION_COOKIE_HTTPONLY = True (не доступно JS)SESSION_COOKIE_SAMESITE = 'Lax' или 'Strict' (предпочтительно Lax для большинства)Flask по умолчанию хранит сессии в client‑signed cookies. Для большей безопасности храните сессии на сервере (Redis/DB) через Flask-Session:
from flask_session import Session
app.config['SESSION_TYPE'] = 'redis'
Session(app)Защита от фиксации сессий:
Генерируйте новый идентификатор сессии при логине (session.clear() и заполнение новой сессии).На логаут обязательно удаляйте сессию на сервере и cookie.Ограничение времени жизни:
app.permanent_session_lifetime = timedelta(hours=1)Используйте короткие тайм‑ауты для чувствительных операций и опцию "запомнить меня" с долгосрочной токенизацией, а не долговременными сессионными куки.Аутентификация:
Используйте Flask-Login для управления логином/логаутом, и храните только идентификатор пользователя в сессии.Для API — используйте JWT аккуратно (реплэй/отзыв проблемны) или токены доступа + refresh токены, хранимые в httpOnly cookie и с проверкой на сервере.
6) Дополнительные рекомендации
HTTPS всегда.Ограничение частоты (rate limiting) и защита от брутфорса (Flask-Limiter, блокировки, задержки).Логирование не содержит секретов (не логируйте пароли, токены, полные запросы с секр. параметрами).Мониторинг, IDS/IPS и тестирование (DAST, SAST, регулярные pentest).Минимизация прав БД: приложение использует пользователя БД с минимальными правами (SELECT/INSERT/UPDATE только по необходимости).Принцип наименьших привилегий для секретов и сервисов.Кратко — что нужно сделать прямо сейчас
Заменить конкатенацию SQL на параметризованные запросы / ORM.Экранировать/валидировать пользовательский ввод и эскейпить вывод в шаблонах (Jinja2 по умолчанию).Подключить CSRF‑защиту для форм/изменяющих запросов.Перейти на безопасное хранение паролей (Argon2/bcrypt через библиотеки).Пересмотреть конфигурацию сессий (Secure, HttpOnly, SameSite), и при необходимости перейти на серверные сессии + регенерация сессии при логине.Если хотите, могу:
Привести готовый пример приложения Flask с SQLAlchemy, Argon2 и Flask‑Login, где все эти рекомендации реализованы,Или показать конкретные примеры под вашу СУБД (Postgres/SQLite/MySQL).