Sentry/GlitchTip scrub-хук навешен только на transaction-события (before_send_transaction), error-события не редактятся — apiKey из URL/spans/breadcrumbs утекает в трекер #1567

Closed
opened 2026-06-16 17:05:37 +00:00 by bot-backend · 0 comments
Collaborator

Severity: medium · Категория: security/PII-leak · Файл: backend/app/main.py:54-71 (init); see backend/app/observability/sentry_scrub.py:28-49

main.py:62 и workers/celery_app.py:33: sentry_sdk.init() передаёт scrub_sensitive_query ТОЛЬКО как before_send_transaction. Хука before_send для ERROR-событий нет (grep 'before_send' по backend/app — только две строки before_send_transaction). before_send_transaction в Sentry SDK вызывается ИСКЛЮЧИТЕЛЬНО для transaction (performance) events, а НЕ для error events. Следствие: scrub_sensitive_query (observability/sentry_scrub.py:28-49), умеющий редактить event['request']['url'] и span.data['url'/'http.url'/'http.target'] + span['description'], на error-событиях НЕ запускается.

ИМПАКТ. scrapers/objective.py:135-139 делает httpx GET {base_url}/Users/User/GetToken с params={'apiKey': self.api_key} → исходящий URL содержит ?apiKey=. При активных HttpxIntegration() и LoggingIntegration(event_level=ERROR) этот URL попадает в span.data['http.url'] и в breadcrumbs. Любое исключение/ERROR-лог в окне такого запроса (например ObjectiveAuthError, 5xx на fetch-пути) формирует ERROR-событие с НЕредактированными spans/breadcrumbs, содержащими apiKey=. send_default_pii=False режет headers/cookies/body/user, но НЕ http.url в span-data и НЕ значения query-параметров. Transaction-only scrubber даёт ложное ощущение, что ключи скраббятся, тогда как именно error-путь (самый вероятный носитель упавшего запроса) не покрыт. Бласт-радиус ограничен self-hosted errors.gendsgn.ru (внутренний контур), но долгоживущий apiKey Объектива в error-breadcrumbs — реальная дыра гигиены секретов, пропущенная #79.

ФИКС: добавить before_send=scrub_sensitive_query в оба init (main.py и celery_app.py) и расширить scrub_sensitive_query, чтобы он чистил также breadcrumbs (event['breadcrumbs']['values'][*]['data']['url'] и 'message'), поскольку HttpxIntegration пишет URL именно в breadcrumbs error-события. Дополнительно/альтернативно: перестать слать apiKey в query-string в objective.py (перенести в заголовок, если API это допускает).

Почему баг: send_default_pii=False suppresses bodies/cookies/headers/user but the Sentry SDK does NOT treat the request URL (including its query string) as PII — request.url with query params is still attached to error events. The codebase passes secrets in query strings: Objective.ru auth is GET /Users/User/GetToken?apiKey= (config.py:128-130), and admin triggers are authorized via tokens. Any unhandled exception during such a request (e.g. a 5xx on the Objective fetch path, or any error logged at ERROR level while such a URL is on the active scope) ships the full URL with apiKey=/token= to GlitchTip unredacted. The transaction-only scrubber gives a false sense that 'secrets are scrubbed' (the init comment claims keys won't leak), but the error-event path — the one most likely to contain the failing request — is uncovered. This is a real residual leak surface that the prior #79 PII pass and the scrub hook missed.

Как чинить: Wire the same redaction as before_send (not only before_send_transaction): scrub_sensitive_query already operates on event['request']['url'] and event['spans'], so set before_send=scrub_sensitive_query in sentry_sdk.init() too (or a shared wrapper). Optionally extend the regex to also catch the path-style GetToken?apiKey= and confirm error events route through it.


deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.72 · unit B06

**Severity:** medium · **Категория:** security/PII-leak · **Файл:** `backend/app/main.py:54-71 (init); see backend/app/observability/sentry_scrub.py:28-49` main.py:62 и workers/celery_app.py:33: sentry_sdk.init() передаёт scrub_sensitive_query ТОЛЬКО как before_send_transaction. Хука before_send для ERROR-событий нет (grep 'before_send' по backend/app — только две строки before_send_transaction). before_send_transaction в Sentry SDK вызывается ИСКЛЮЧИТЕЛЬНО для transaction (performance) events, а НЕ для error events. Следствие: scrub_sensitive_query (observability/sentry_scrub.py:28-49), умеющий редактить event['request']['url'] и span.data['url'/'http.url'/'http.target'] + span['description'], на error-событиях НЕ запускается. ИМПАКТ. scrapers/objective.py:135-139 делает httpx GET {base_url}/Users/User/GetToken с params={'apiKey': self.api_key} → исходящий URL содержит ?apiKey=<KEY>. При активных HttpxIntegration() и LoggingIntegration(event_level=ERROR) этот URL попадает в span.data['http.url'] и в breadcrumbs. Любое исключение/ERROR-лог в окне такого запроса (например ObjectiveAuthError, 5xx на fetch-пути) формирует ERROR-событие с НЕредактированными spans/breadcrumbs, содержащими apiKey=<KEY>. send_default_pii=False режет headers/cookies/body/user, но НЕ http.url в span-data и НЕ значения query-параметров. Transaction-only scrubber даёт ложное ощущение, что ключи скраббятся, тогда как именно error-путь (самый вероятный носитель упавшего запроса) не покрыт. Бласт-радиус ограничен self-hosted errors.gendsgn.ru (внутренний контур), но долгоживущий apiKey Объектива в error-breadcrumbs — реальная дыра гигиены секретов, пропущенная #79. ФИКС: добавить before_send=scrub_sensitive_query в оба init (main.py и celery_app.py) и расширить scrub_sensitive_query, чтобы он чистил также breadcrumbs (event['breadcrumbs']['values'][*]['data']['url'] и 'message'), поскольку HttpxIntegration пишет URL именно в breadcrumbs error-события. Дополнительно/альтернативно: перестать слать apiKey в query-string в objective.py (перенести в заголовок, если API это допускает). **Почему баг:** send_default_pii=False suppresses bodies/cookies/headers/user but the Sentry SDK does NOT treat the request URL (including its query string) as PII — request.url with query params is still attached to error events. The codebase passes secrets in query strings: Objective.ru auth is GET /Users/User/GetToken?apiKey=<KEY> (config.py:128-130), and admin triggers are authorized via tokens. Any unhandled exception during such a request (e.g. a 5xx on the Objective fetch path, or any error logged at ERROR level while such a URL is on the active scope) ships the full URL with apiKey=<secret>/token=<secret> to GlitchTip unredacted. The transaction-only scrubber gives a false sense that 'secrets are scrubbed' (the init comment claims keys won't leak), but the error-event path — the one most likely to contain the failing request — is uncovered. This is a real residual leak surface that the prior #79 PII pass and the scrub hook missed. **Как чинить:** Wire the same redaction as before_send (not only before_send_transaction): scrub_sensitive_query already operates on event['request']['url'] and event['spans'], so set before_send=scrub_sensitive_query in sentry_sdk.init() too (or a shared wrapper). Optionally extend the regex to also catch the path-style GetToken?apiKey= and confirm error events route through it. --- <sub>deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.72 · unit B06</sub>
bot-backend added the
week ревью 1
label 2026-06-16 17:05:37 +00:00
Sign in to join this conversation.
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#1567
No description provided.