#3194: ключ шифрования кук и сами куки больше не уезжают в GlitchTip #3275

Merged
bot-backend merged 2 commits from fix/3194-hide-sql-parameters into main 2026-08-30 10:42:01 +00:00
Collaborator

Закрывает #3194.

Дефект

Сервисы сессий передают ключ шифрования bind-параметром:

db.execute(text("... pgp_sym_encrypt(:cookies_json, :key) ..."),
           {"cookies_json": ..., "key": settings.cookie_encryption_key})

Любое исключение из db.execute SQLAlchemy оборачивает в StatementError, а текст этого исключения содержит все bind-параметры — и уезжает в GlitchTip вместе с ключом и живыми авторизационными куками.

Поверхность оказалась шире, чем в issue

Аудит нашёл на обеих осях больше, чем было названо:

в issue по факту
движков 1 (app/core/db.py:8) 5 — два продукта × app + auth, плюс alembic
копий сервиса сессий 3 4 — есть ещё packages/scraper-kit/providers/cian/session.py:218

Правка сделана на уровне движка, а не по сайтам вызова: hide_parameters=True на всех пяти. Это кроет и четвёртую копию, которую issue не знает, и всё будущее — латать по одному месту значило бы повторить ровно ту ошибку, за которую сам репозиторий уже расплатился удалённым allowlist'ом scraper в #2679.

Отдельно: scheduler_main.py был единственным из трёх sentry_sdk.init без include_local_variables=False (соседи ставят: main.py:123, tgbot_main.py:100). Это процесс скрейпера, в локальных переменных кадров лежат прокси-креды вида user:pass. Добавлено.

Чего правка НЕ закрывает

Написано в комментариях у самих правок, чтобы не читалось как «теперь всё безопасно»:

  • текст ошибки самого драйвера (Postgres DETAIL: Key (col)=(value)) — он собирается ДО гейта hide_parameters;
  • сырые psycopg-подключения мимо движков.

Аудит проверил оба: секретов на этих путях сейчас нет (данные публичные, скрейпленные), но механизм остаётся открытым.

Утекло ли уже

Проверено фактом, а не рассуждением: полный скан боевого GlitchTip — 31 417 событий по всем партициям и 7 712 issues, подстроки [parameters:, pgp_sym_encrypt, cookies_json, COOKIE_ENCRYPTION, сам ключ и его 16-символьный префикс. Пересечений ноль.

Честная граница вывода: окно только с 31.05.2026. Скептик отдельно отметил, что 91 день одинаково объясняется и ретенцией, и тем, что таблицу завели в эту дату — маркер не различает. Плюс искали текущее значение ключа, а он ротируемый. Про период до 31.05 сказать нечего — если владелец считает риск неприемлемым, ротация остаётся его решением.

Проверки

Тест test_3194_hide_sql_parameters.py, 3 кейса. Отказ провоцируется на уровне запроса (живой sqlite, SELECT :key FROM no_such_table_3194), и ассерт на имя таблицы доказывает, что дело дошло до bind-параметров, а не упало на CONNECT — я сегодня уже обжёгся ровно на такой тавтологии, когда обе ветки моей ручной пробы напечатали «секрета нет» просто потому, что соединение падало раньше.

Фальсификация дважды, автором и ревьюером независимо: снятие флага даёт красное по значению (assert False is True), не ImportError.

pytest 9 passed · ruff check чисто по всем 7 файлам · хуки зелёные, --no-verify не использовался.

Известный пробел (от ревьюера): проводочный тест стоит только на движке МЕРЫ — снятие флага в backend/app/core/ ничего не покрасит. Механизм общий, но симметричного сторожа у ПТИЦЫ нет.

Закрывает #3194. ## Дефект Сервисы сессий передают ключ шифрования bind-параметром: ```python db.execute(text("... pgp_sym_encrypt(:cookies_json, :key) ..."), {"cookies_json": ..., "key": settings.cookie_encryption_key}) ``` Любое исключение из `db.execute` SQLAlchemy оборачивает в `StatementError`, а **текст этого исключения содержит все bind-параметры** — и уезжает в GlitchTip вместе с ключом и живыми авторизационными куками. ## Поверхность оказалась шире, чем в issue Аудит нашёл на обеих осях больше, чем было названо: | | в issue | по факту | |---|---|---| | движков | 1 (`app/core/db.py:8`) | **5** — два продукта × app + auth, плюс alembic | | копий сервиса сессий | 3 | **4** — есть ещё `packages/scraper-kit/providers/cian/session.py:218` | Правка сделана **на уровне движка**, а не по сайтам вызова: `hide_parameters=True` на всех пяти. Это кроет и четвёртую копию, которую issue не знает, и всё будущее — латать по одному месту значило бы повторить ровно ту ошибку, за которую сам репозиторий уже расплатился удалённым allowlist'ом `scraper` в #2679. Отдельно: `scheduler_main.py` был **единственным из трёх** `sentry_sdk.init` без `include_local_variables=False` (соседи ставят: `main.py:123`, `tgbot_main.py:100`). Это процесс скрейпера, в локальных переменных кадров лежат прокси-креды вида `user:pass`. Добавлено. ## Чего правка НЕ закрывает Написано в комментариях у самих правок, чтобы не читалось как «теперь всё безопасно»: - текст ошибки самого драйвера (Postgres `DETAIL: Key (col)=(value)`) — он собирается ДО гейта `hide_parameters`; - сырые psycopg-подключения мимо движков. Аудит проверил оба: секретов на этих путях сейчас нет (данные публичные, скрейпленные), но механизм остаётся открытым. ## Утекло ли уже Проверено фактом, а не рассуждением: полный скан боевого GlitchTip — 31 417 событий по всем партициям и 7 712 issues, подстроки `[parameters:`, `pgp_sym_encrypt`, `cookies_json`, `COOKIE_ENCRYPTION`, сам ключ и его 16-символьный префикс. **Пересечений ноль.** Честная граница вывода: окно только с 31.05.2026. Скептик отдельно отметил, что 91 день одинаково объясняется и ретенцией, и тем, что таблицу завели в эту дату — маркер не различает. Плюс искали текущее значение ключа, а он ротируемый. **Про период до 31.05 сказать нечего** — если владелец считает риск неприемлемым, ротация остаётся его решением. ## Проверки Тест `test_3194_hide_sql_parameters.py`, 3 кейса. Отказ провоцируется **на уровне запроса** (живой sqlite, `SELECT :key FROM no_such_table_3194`), и ассерт на имя таблицы доказывает, что дело дошло до bind-параметров, а не упало на CONNECT — я сегодня уже обжёгся ровно на такой тавтологии, когда обе ветки моей ручной пробы напечатали «секрета нет» просто потому, что соединение падало раньше. Фальсификация дважды, автором и ревьюером независимо: снятие флага даёт красное **по значению** (`assert False is True`), не ImportError. `pytest` 9 passed · `ruff check` чисто по всем 7 файлам · хуки зелёные, `--no-verify` не использовался. **Известный пробел** (от ревьюера): проводочный тест стоит только на движке МЕРЫ — снятие флага в `backend/app/core/` ничего не покрасит. Механизм общий, но симметричного сторожа у ПТИЦЫ нет.
bot-backend added 1 commit 2026-08-30 10:04:25 +00:00
fix(#3194): hide_parameters=True на всех движках, include_local_variables=False у scheduler
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI / openapi-codegen-check (pull_request) Successful in 3m10s
CI Trade-In / backend-tests (pull_request) Successful in 5m47s
CI / backend-tests (pull_request) Successful in 18m3s
7e59c1e5b0
Ключ шифрования кук и сами куки уезжали в GlitchTip: сервисы сессий передают
их bind-параметрами в pgp_sym_encrypt(:cookies_json, :key), а SQLAlchemy при
ошибке печатает ВСЕ параметры в тексте StatementError.

Правка на уровне движка (backend + tradein-mvp: db.py, auth_db.py,
alembic/env.py) кроет все сайты вызова разом, включая четвёртую копию в
scraper-kit и всё будущее.

scheduler_main.py был единственным из трёх sentry_sdk.init без
include_local_variables=False — процесс скрейпера, в кадрах лежат прокси-креды.

НЕ закрывает: текст ошибки самого драйвера (Postgres DETAIL со значением) и
сырые psycopg-подключения мимо движков — отдельный класс.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend added 1 commit 2026-08-30 10:23:08 +00:00
Merge branch 'main' into fix/3194-hide-sql-parameters
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 19s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 3m10s
CI Trade-In / backend-tests (pull_request) Successful in 5m56s
CI / backend-tests (pull_request) Successful in 18m10s
8fb5587888
bot-backend merged commit de31f2791b into main 2026-08-30 10:42:01 +00:00
bot-backend deleted branch fix/3194-hide-sql-parameters 2026-08-30 10:42:01 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3275
No description provided.