fix(tradein): секрет вебхука GlitchTip не попадает в access-log — скруббер query-секретов + приём из заголовка #3353

Merged
bot-backend merged 2 commits from fix/3154-glitchtip-secret-log-leak into main 2026-09-05 18:13:00 +00:00
Collaborator

Refs #3154 (security p2). НЕ закрывает issue — нужна ротация секрета на проде и второй слой в Alloy (ниже).

Факты

URL вебхука в репо не хранится — живёт в самом GlitchTip (Alert Recipient, alerts_alertrecipient). GlitchTip 6.1.6 send_webhook() = session.post(url, json=...) без headers — заголовок он не шлёт (факт из докстринга модуля в репо, не независимый замер).

Слой 1 — приём из заголовка

Хендлер принимает X-GlitchTip-Secret; query ?secret= остаётся fallback'ом, пока отправитель не умеет заголовок (переезд возможен через Caddy header_up + снятие query на маршруте — отдельная devops-задача). Сравнение уже было secrets.compare_digest.

Слой 2 — скруббер (app/core/log_scrub.py)

QuerySecretFilter маскирует значения secret|token|api_key|apikey|access_token|password|signature|sig в query-строке; вешается на uvicorn.access (свой handler, propagate=False) и на handler'ы корня. Одна строка в main.py после basicConfig.

Тесты

test_3154_query_secret_scrub.py (13) + правка test_glitchtip_webhook.py: 75 passed (-k). Фальсификация — нейтрализация скруббера (return text): AssertionError: … glitchtip-webhook?secret=5fc281… — секрет в строке, по значению.

Что остаётся (не в этом PR)

  1. Ротация TRADEIN_INTERNAL_AUTH_SECRET — он уже лежит в Loki открытым (ретенция 30 суток), фикс прошлое не чистит; менять одновременно в рантайм-конфиге прода и в URL получателей GlitchTip.
  2. Второй stage.replace в Alloy (ops/metrics/alloy/alloy-apps.alloy, скруббер #3115) — defence-in-depth, devops.
Refs #3154 (security p2). НЕ закрывает issue — нужна ротация секрета на проде и второй слой в Alloy (ниже). ## Факты URL вебхука в репо не хранится — живёт в самом GlitchTip (Alert Recipient, `alerts_alertrecipient`). GlitchTip 6.1.6 `send_webhook()` = `session.post(url, json=...)` без headers — заголовок он не шлёт (факт из докстринга модуля в репо, не независимый замер). ## Слой 1 — приём из заголовка Хендлер принимает `X-GlitchTip-Secret`; query `?secret=` остаётся fallback'ом, пока отправитель не умеет заголовок (переезд возможен через Caddy `header_up` + снятие query на маршруте — отдельная devops-задача). Сравнение уже было `secrets.compare_digest`. ## Слой 2 — скруббер (`app/core/log_scrub.py`) `QuerySecretFilter` маскирует значения `secret|token|api_key|apikey|access_token|password|signature|sig` в query-строке; вешается на `uvicorn.access` (свой handler, `propagate=False`) и на handler'ы корня. Одна строка в `main.py` после basicConfig. ## Тесты `test_3154_query_secret_scrub.py` (13) + правка `test_glitchtip_webhook.py`: `75 passed` (-k). Фальсификация — нейтрализация скруббера (`return text`): `AssertionError: … glitchtip-webhook?secret=5fc281…` — секрет в строке, по значению. ## Что остаётся (не в этом PR) 1. **Ротация `TRADEIN_INTERNAL_AUTH_SECRET`** — он уже лежит в Loki открытым (ретенция 30 суток), фикс прошлое не чистит; менять одновременно в рантайм-конфиге прода и в URL получателей GlitchTip. 2. Второй `stage.replace` в Alloy (`ops/metrics/alloy/alloy-apps.alloy`, скруббер #3115) — defence-in-depth, devops.
bot-backend added 1 commit 2026-09-05 17:55:10 +00:00
fix(tradein): не логировать секрет вебхука GlitchTip в access-log (#3154)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI / changes (pull_request) Successful in 14s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m9s
ed94a03f73
uvicorn печатает в access-log полный путь вместе с query, поэтому секрет
вебхука (`?secret=`, он же TRADEIN_INTERNAL_AUTH_SECRET, второй рубеж rbac)
уезжал в Loki открытым текстом. Скруббер #3115 в Alloy ловит только форму
`user:pass@host` (DSN postgres_exporter) и такую строку не закрывает.

Два слоя:
- хендлер принимает секрет из заголовка X-GlitchTip-Secret; query-параметр
  остаётся fallback'ом, т.к. сам GlitchTip 6.1.6 (`send_webhook()`)
  заголовков не шлёт вовсе — убрать query можно только когда заголовок начнёт
  подставлять кто-то перед нами (Caddy header_up) или сменится отправитель;
- app/core/log_scrub.py: logging-фильтр маскирует значения чувствительных
  query-параметров (secret/token/api_key/…) на uvicorn.access и на
  обработчиках корневого логгера — секрета нет уже в `docker logs`.

Сравнение секрета и было constant-time (`secrets.compare_digest`).
Light1YT added 1 commit 2026-09-05 18:03:23 +00:00
fix(tradein): маскировать секрет и в параметрах с префиксом (#3154)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m38s
55a67e5c45
Ревью-minor: якорь [?&] вплотную к имени пропускал client_secret=,
refresh_token=, auth_token=, webhook_secret=. Разрешаем префикс [\w.-]*
перед альтернацией — маскируем имена, ОКАНЧИВАЮЩИЕСЯ на чувствительное
слово. Кейс not_a_secret_name заменён на честные отрицательные:
?secretary= и ?tokens_page= (не оканчиваются на secret/token) плюс
/api/v1/token-info в пути.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bot-backend merged commit b8084f6afe into main 2026-09-05 18:13:00 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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#3353
No description provided.