Сессии, куки и flash-сообщения

HTTP не помнит пользователя между запросами. Сессии и куки добавляют «память»: сервер узнаёт вернувшегося пользователя и хранит для него данные.
Каждый HTTP-запрос независим — сервер по умолчанию не знает, кто к нему пришёл. Кука — маленький файл у клиента, который браузер шлёт с каждым запросом. session во Flask — словарь, который сохраняется в подписанной куке, поэтому пользователь не может его подделать.

HTTP без состояния (stateless): два запроса от одного человека для сервера — два незнакомца. Чтобы «узнавать» пользователя (вошёл в систему, корзина покупок), нужна память. Её дают куки — пары ключ-значение, которые сервер просит браузер хранить и присылать обратно.

Flask поверх кук даёт удобный объект session — он выглядит как словарь, но автоматически сохраняется в подписанной куке на стороне клиента:

from flask import session

@app.route("/login", methods=["POST"])
def login():
    session["user_id"] = 42        # сохранится в куке
    return redirect(url_for("profile"))

@app.route("/logout")
def logout():
    session.pop("user_id", None)   # очистить
    return redirect(url_for("index"))

Важно: session подписана SECRET_KEY, но не зашифрована — клиент видит её содержимое, но не может подменить (подпись не сойдётся). Поэтому в session нельзя класть секреты, только идентификаторы.

Flash-сообщения — частный случай: одноразовое сообщение, которое показывается на следующей странице (например «Вы вошли»). flash кладёт его в session, get_flashed_messages забирает — и оно исчезает.

from flask import flash
flash("Профиль сохранён", "success")
# в шаблоне: {% for msg in get_flashed_messages() %}...{% endfor %}

Понимание, что session по умолчанию хранится у клиента в подписанной куке, снимает множество вопросов и предостерегает от ошибок. Подпись гарантирует целостность — клиент не подменит значения, — но не конфиденциальность: содержимое куки читаемо, поэтому секреты туда не кладут, только идентификаторы. Размер куки ограничен примерно четырьмя килобайтами, так что большие данные в session не поместятся — для них хранят на сервере по id из сессии. Куки стоит сопровождать защитными флагами: HttpOnly (недоступна из JavaScript, защита от кражи через XSS), Secure (только по HTTPS), SameSite (ограничивает отправку с чужих сайтов, помогает против CSRF). Flash — частный изящный приём поверх сессии для одноразовых уведомлений.

Как работает под капотом

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

  Запрос 1 (login)
     │ session["user_id"]=42
     ▼ сервер подписывает: данные + подпись(SECRET_KEY)
  Set-Cookie: session=<данные.подпись>
     ▼ браузер хранит
  Запрос 2 (любой)
     │ браузер шлёт куку обратно
     ▼ сервер проверяет подпись → восстанавливает session
  подпись не сошлась? → данные отброшены (подмена)

Смоделируем подписанную сессию обычным Python.

import hashlib

SECRET = "my-secret-key"

def sign(data):
    sig = hashlib.sha256((data + SECRET).encode()).hexdigest()[:12]
    return data + "." + sig

def verify(cookie):
    data, sig = cookie.rsplit(".", 1)
    expected = hashlib.sha256((data + SECRET).encode()).hexdigest()[:12]
    return data if sig == expected else None

cookie = sign("user_id=42")
print("кука:", cookie)
print("честная:", verify(cookie))
print("подделка:", verify("user_id=999." + cookie.split(".")[1]))

Запусти: честная кука восстанавливается, подделанная (изменили user_id) отвергается — подпись не сходится. Так Flask доверяет данным из session, не храня их у себя.

Частые ошибки

  • Класть секреты в session. Клиент видит содержимое. Храни id, а не пароли/токены.
  • Забыть get_flashed_messages в шаблоне. Тогда flash-сообщения копятся и не показываются.
  • Думать, что session на сервере. По умолчанию она в куке у клиента (ограничена ~4 КБ).

Best practices

  • В session — только идентификаторы; чувствительные данные держи на сервере по id.
  • Ставь куки флаги Secure/HttpOnly/SameSite для защиты.
  • Flash используй для одноразовых уведомлений, не для постоянных данных.

Что запомнить

  • Куки дают HTTP «память» между запросами.
  • session — подписанный SECRET_KEY словарь, по умолчанию в куке у клиента.
  • Подпись защищает от подмены, но не шифрует — храни только идентификаторы.
  • flash — одноразовые сообщения, забираемые через get_flashed_messages.

Итог: куки дают HTTP память, session — удобный подписанный словарь в куке (виден, но не подделываем), flash — одноразовые сообщения. Это завершает блок про состояние; дальше переходим к базам данных.

Проверьте себя
1. Где по умолчанию хранится содержимое session во Flask?
AВ базе данных на сервере
BВ подписанной куке на стороне клиента
CВ оперативной памяти сервера навсегда
DВ файле на диске сервера
2. Почему в session нельзя класть секретные данные?
AНе хватит места
BSession подписана, но не зашифрована — клиент видит содержимое
CЭто замедляет сервер
DFlask это запрещает технически