Это краткий чек-лист по укреплению безопасности Flask с фокусом на код, который поможет защитить приложение.
Краткий чек-лист
- Секреты в переменных окружения или хранилище секретов (никогда не хранить в git)
- HTTPS везде + HSTS (от 1 года и более)
- Заголовки безопасности (CSP, X-Frame-Options, Referrer-Policy и т. д.)
- Строгие cookie и сессии (Secure, HttpOnly, SameSite)
- Защита CSRF для маршрутов, изменяющих состояние
- Аутентификация с сильным хешированием (argon2/bcrypt) и MFA, где это возможно
- Ограничение частоты запросов для аутентификации и чувствительных конечных точек
- Валидация входных данных и безопасная загрузка файлов
- Фиксация версий зависимостей и сканирование безопасности (pip-audit, bandit)
- Корректный reverse proxy + WSGI (gunicorn/uwsgi) и контейнеры без root
- Структурированные журналы аудита + оповещения
1) Конфигурация и секреты
# app/config.py
import os
class Settings:
secret_key = os.environ["FLASK_SECRET_KEY"]
session_cookie_secure = True
session_cookie_httponly = True
session_cookie_samesite = "Lax" # или "Strict" для приложений в рамках одного сайта
remember_cookie_duration = 0
settings = Settings()
Используйте менеджер секретов (AWS Secrets Manager, GCP Secret Manager, 1Password Connect) или хотя бы .env (только для разработки). Никогда не добавляйте секреты в git.
# .env (только для разработки)
FLASK_SECRET_KEY='change-me'
DATABASE_URL='postgresql://user:pass@localhost:5432/app'
2) HTTPS + HSTS (на уровне приложения)
flask-talisman — это легковесное расширение Flask, которое устанавливает для вас адекватные заголовки безопасности HTTP и может принудительно включать HTTPS/HSTS. Почему это важно:
- Предотвращает понижение протокола и проблемы со смешанным содержимым (HSTS)
- Снижает риск clickjacking и утечки информации (X‑Frame‑Options, Referrer‑Policy)
- Предоставляет настраиваемую базовую CSP для ограничения XSS
Примечания:
-
Отключите принудительный HTTPS в локальной разработке или установите
app.debug = True. -
При работе за proxy убедитесь, что установлен
X-Forwarded-Proto, чтобы Talisman мог определить HTTPS. -
PyPI: Flask-Talisman
-
Установка:
pip install flask-talisman
# app/security.py
from flask import Flask
from flask_talisman import Talisman
csp = {
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-inline'"], # замените на nonces ниже для более строгой CSP
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:'],
}
def apply_security(app: Flask) -> None:
Talisman(
app,
content_security_policy=csp,
force_https=True,
strict_transport_security=True,
strict_transport_security_max_age=31536000,
frame_options='SAMEORIGIN',
referrer_policy='no-referrer',
session_cookie_secure=True,
session_cookie_http_only=True,
)
3) Сессии и cookie
Для чувствительных приложений отдавайте предпочтение сессиям на стороне сервера (например, Redis), а не подписанным cookie на стороне клиента.
Почему:
- Сессии на стороне сервера позволяют мгновенно делать сессии недействительными (выход из системы, отзыв доступа)
- Вы избегаете больших, защищённых от подделки, но всё ещё допускающих повторное воспроизведение cookie на стороне клиента
- Проще безопасно хранить дополнительный контекст аутентификации (роли, токены защиты от повторного воспроизведения)
Если вы вынуждены использовать сессии в cookie, делайте cookie краткоживущими, устанавливайте Secure+HttpOnly+SameSite и ротируйте ключи.
Библиотека: Flask-Session
Установка:
pip install Flask-Session
from flask import Flask
from flask_session import Session
def configure_sessions(app: Flask) -> None:
app.config.update(
SESSION_TYPE='redis',
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
PERMANENT_SESSION_LIFETIME=0,
)
Session(app)
4) Защита CSRF
Библиотека: Flask-WTF
Установка:
pip install flask-wtf
Почему:
- CSRF использует cookie браузера; токены связывают действие пользователя с формой или страницей
- SameSite=Lax помогает, но недостаточно для всех сценариев (например, некоторых OAuth/POST)
from flask_wtf import CSRFProtect
csrf = CSRFProtect()
def enable_csrf(app):
app.config['WTF_CSRF_TIME_LIMIT'] = None
csrf.init_app(app)
5) Аутентификация, хеширование и ограничение частоты запросов
Библиотеки:
- Хеширование:
werkzeug.security(встроенные PBKDF2/Scrypt). Для Argon2 используйте argon2-cffi - Ограничение частоты запросов: Flask-Limiter
Установка:
pip install Flask-Limiter
from werkzeug.security import generate_password_hash, check_password_hash
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(key_func=get_remote_address, default_limits=["200/min"])
def hash_password(pw: str) -> str:
return generate_password_hash(pw, method='scrypt') # или 'pbkdf2:sha256', 'bcrypt', 'argon2'
def verify_password(hash_: str, pw: str) -> bool:
return check_password_hash(hash_, pw)
def protect_auth(app):
limiter.init_app(app)
@app.post('/login')
@limiter.limit("5/minute")
def login():
...
Почему:
- Замедляет атаки credential stuffing и brute-force (Limiter)
- Храните только хеши паролей; никогда не храните пароли в открытом виде. Отдавайте предпочтение memory-hard KDF
Необязательно (Argon2):
pip install argon2-cffi
from argon2 import PasswordHasher
ph = PasswordHasher()
hash_ = ph.hash(password)
ph.verify(hash_, candidate)
6) Валидация входных данных и загрузка файлов
Используйте pydantic/marshmallow, чтобы своевременно отклонять некорректные входные данные. Для файлов: проверяйте MIME по содержимому (magic), устанавливайте ограничения по размеру и разрешённым расширениям и рандомизируйте имена файлов.
Библиотеки:
- pydantic или marshmallow
- pydantic: быстрая валидация, модели на основе типов
- marshmallow: явные схемы и контроль сериализации
Установите одну:
pip install pydantic
# или
pip install marshmallow
from pydantic import BaseModel, EmailStr, constr
class RegisterBody(BaseModel):
email: EmailStr
password: constr(min_length=12)
7) Content Security Policy (паттерн nonce)
Почему:
- Строгая CSP с nonces блокирует внедрение inline/script и большинство отражённых XSS
- Nonces работают лучше хешей, когда содержимое меняется при каждом запросе
from flask import g
import secrets
@app.before_request
def set_nonce():
g.csp_nonce = secrets.token_urlsafe(16)
# Jinja: <script nonce="{{ g.csp_nonce }}">...</script>
Обновите CSP, добавив script-src 'self' 'nonce-{value}', и создавайте новый nonce для каждого запроса; замените все inline-обработчики событий на обычные скрипты.
8) Гигиена зависимостей (с фокусом на код)
pip install --upgrade pip pip-tools pip-audit bandit
pip-compile -o requirements.txt pyproject.toml # или зафиксируйте версии с помощью pip-tools
pip-audit
bandit -r app/
9) Наблюдаемость и журналы
Формируйте журналы в JSON и отправляйте их в свою SIEM. Рассмотрите Sentry или OpenTelemetry для трейсов.
import json, logging, sys
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter('%(message)s'))
logger = logging.getLogger('app')
logger.setLevel(logging.INFO)
logger.addHandler(handler)
def audit(event: str, **kwargs):
logger.info(json.dumps({'event': event, **kwargs}))
10) Безопасные значения по умолчанию, которые следует установить
app.config.update(
JSONIFY_PRETTYPRINT_REGULAR=False,
PROPAGATE_EXCEPTIONS=False,
MAX_CONTENT_LENGTH=10 * 1024 * 1024, # загрузки размером 10 MB
SEND_FILE_MAX_AGE_DEFAULT=31536000,
)
11) Дополнительные фрагменты для укрепления безопасности
Добавьте ограничения размера запросов, отключите утечки серверных ошибок и очищайте заголовки.
from flask import Flask, request
def extra_hardening(app: Flask):
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024
@app.after_request
def remove_server_header(resp):
resp.headers.pop('Server', None)
return resp
@app.before_request
def block_weird_methods():
if request.method not in { 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS' }:
return ("", 405)
Финальный чек-лист перед запуском
- TLS + HSTS включены и проверены
- CSP применяется (сначала только отчёты, затем принудительное применение)
- Cookie: Secure+HttpOnly+SameSite, минимальный срок жизни сессии
- CSRF для всех небезопасных методов
- Ограничения частоты запросов для маршрутов аутентификации и чувствительных маршрутов
- Версии зависимостей зафиксированы,
pip-auditбез проблем, результатыbanditпроверены - Журналы структурированы и отправляются; оповещения настроены