gendesign/tradein-mvp/backend/data/sql/274_yandex_session_cookies.sql
bot-backend c995af73c2
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
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 4m44s
docs(tradein/scrapers): в репозитории лежало опровергнутое обоснование хранилища сессий Яндекса
#3195 замержен с утверждением, что авторизованная сессия раскрывает контакты
продавца. Утверждение неверно, а лежит оно в двух местах, которые читают в первую
очередь: докстринг app/services/yandex_session.py и шапка миграции 274.

Повторный замер (#3192, 2026-08-28) сделан на ПРОД-транспорте — curl_cffi + прокси
из пула, тот же путь, что у yandex_detail_backfill, — а не на сайдкаре, как первый:

- offerCard.card.author у целевой карточки не несёт phones/phoneNumbers ни в одном
  из 12 случайных объявлений (6 AGENCY, 6 DEVELOPER), одинаково с куками и без;
  только encryptedPhones (1 токен) и redirectPhones;
- phoneNumbers во всём INITIAL_STATE встречается только под
  offerCard.visitedOffers[*].author — истории просмотров НАШЕЙ учётки; анонимно
  список пуст, с куками в нём 9-10 записей;
- первый замер («0 → 3,4,5,6») считал рост именно этой истории: +1 на каждый фетч;
- authorStats.phones (коммутатор застройщика) отдаётся анонимно — тот же номер
  в обеих ветках.

Правка только текстовая: ни схема, ни поведение не меняются. Шапку применённой
миграции правлю сознательно — файл повторно не выполняется (учёт по имени в
_schema_migrations), а неверное описание пережило бы любой следующий разбор.

Таблицу не трогаю: она пуста, но DROP без явного решения владельца делать нельзя.
Судьба #3195 — на владельце, #3192 помечен needs-human.

Refs #3192, #3195
2026-08-28 22:52:47 +03:00

63 lines
4.1 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 274_yandex_session_cookies.sql
-- Purpose: Encrypted storage for Yandex.Nedvizhimost browser session cookies.
-- Uses pgcrypto pgp_sym_encrypt for AES encryption at rest (зеркалит
-- 027_cian_session_cookies.sql / 174_domclick_session_cookies.sql).
--
-- ⚠️ ОБОСНОВАНИЕ ЭТОЙ ТАБЛИЦЫ ОПРОВЕРГНУТО (#3192, замер 2026-08-28).
-- Шапка этого файла утверждала, что авторизованная сессия раскрывает
-- author.phoneNumbers. Повторный замер на прод-транспорте (curl_cffi +
-- прокси, а не сайдкар) показал: у целевой карточки
-- (offerCard.card.author) телефонов нет никогда — 12 из 12 объявлений,
-- с куками и без, только encryptedPhones + redirectPhones.
-- phoneNumbers встречается ТОЛЬКО под offerCard.visitedOffers[*].author
-- — это история просмотров НАШЕЙ учётки, растущая на +1 с каждым
-- фетчем; именно её рост первый замер принял за данные объявления.
-- Таблица создана и пуста. Оставлена как есть: DROP без явного решения
-- владельца не делаем (#3192, label needs-human). Разбор — в тикете и
-- в докстринге app/services/yandex_session.py.
-- Dependencies:
-- - pgcrypto extension (installed here via CREATE EXTENSION IF NOT EXISTS)
-- Deploy order: Apply after 273.
--
-- Security notes:
-- - cookies_encrypted stores AES-encrypted JSON blob via pgp_sym_encrypt.
-- - Encryption key lives in .env.runtime (COOKIE_ENCRYPTION_KEY), never in DB.
-- - Access restricted to admin-token-gated API endpoint only.
--
-- Schema note: ключ записи — account_label (text), НЕ числовой id как у Cian
-- (account_user_id) / DomClick (account_cas_id) — у Яндекса нет внутреннего
-- числового id аккаунта, доступного из cookie-дампа; используем произвольную
-- текстовую метку аккаунта, которую задаёт оператор при заливке дампа.
--
-- Sources: issue #3192 (Yandex session cookies — author.phoneNumbers reveal)
BEGIN;
-- #2752: без lock_timeout CREATE INDEX встанет в очередь за чужой долгой
-- сессией и уведёт за собой запросы приложения — таблица новая и пустая, но
-- очередь блокировок этого не знает. Гейт check-migration-lock-timeout.py
-- держит правило на каждом PR.
SET LOCAL lock_timeout = '5s';
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE IF NOT EXISTS yandex_session_cookies (
account_label text PRIMARY KEY, -- произвольная метка аккаунта (нет числового id)
cookies_encrypted bytea NOT NULL, -- pgp_sym_encrypt(json_cookies, key)
expires_at_estimate timestamptz NOT NULL, -- estimated expiry (~30 days from upload)
uploaded_at timestamptz NOT NULL DEFAULT NOW(),
last_used_at timestamptz, -- set on each successful authenticated fetch
last_invalid_at timestamptz, -- set when session no longer authenticated
notes text -- e.g. 'Account: <label / email>'
);
-- Most recent upload first (admin dashboard ordering)
CREATE INDEX IF NOT EXISTS yandex_cookies_uploaded_idx
ON yandex_session_cookies (uploaded_at DESC);
-- Active session lookup (non-expired and not invalidated)
CREATE INDEX IF NOT EXISTS yandex_cookies_active_idx
ON yandex_session_cookies (expires_at_estimate)
WHERE last_invalid_at IS NULL;
COMMIT;