diff --git a/tradein-mvp/backend/app/services/yandex_session.py b/tradein-mvp/backend/app/services/yandex_session.py index b48fa679..dd5e63f0 100644 --- a/tradein-mvp/backend/app/services/yandex_session.py +++ b/tradein-mvp/backend/app/services/yandex_session.py @@ -1,9 +1,25 @@ """Yandex.Nedvizhimost session cookie management — load/save/invalidate encrypted cookies. -Замер на проде (#3192, сайдкар, две карточки, режимы вперемежку, 2026-08-28): -авторизованная сессия Яндекс.Недвижимости отдаёт ключ `mainPhone` и на 11 больше -уникальных телефонов продавца (90→101 / 89→100), чем анонимный запрос той же -карточки — контакты продавца физически недоступны без валидной сессии. +⚠️ ОБОСНОВАНИЕ ЭТОГО МОДУЛЯ ОПРОВЕРГНУТО (#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 (коммутатор застройщика) отдаётся анонимно — тот же номер + в обеих ветках. + +То есть измеримой разницы по данным объявления авторизация не даёт. Хранилище +работает и покрыто тестами, но пока не имеет доказанного применения и НЕ +заполнено. Судьба модуля — за владельцем (#3192, label needs-human); удалять +таблицу самовольно нельзя. Ниже — исходное описание устройства, оно актуально. Cookies хранятся зашифрованно (pgp_sym_encrypt) в yandex_session_cookies — зеркалит app.services.cian_session / app.services.domclick_session, но: @@ -15,8 +31,8 @@ Cookies хранятся зашифрованно (pgp_sym_encrypt) в yandex_se browser context (следующая часть #3192) требует domain/path, не только value; - НЕТ allowlist-фильтра на сохранение (см. filter_analytics_cookies docstring) — Cian/DomClick фильтруют payload по списку известных cookie-имён перед - сохранением, здесь так делать нельзя: замер выше сделан на ПОЛНОМ наборе из - 44 cookies, и какая из них существенна для mainPhone-раскрытия — неизвестно. + сохранением, здесь так делать нельзя: замеры делались на ПОЛНОМ наборе из + 44 cookies, и какая из них за что отвечает — неизвестно. MVP: без verify_session (аналогично domclick_session) — верификация Yandex-сессии требует реального browser-фетча, не простого curl_cffi-запроса; вне рамок этой @@ -48,10 +64,10 @@ COOKIE_EXPIRY_WARN_DAYS = 5 # ВАЖНО: это НЕ allowlist для фильтрации того, что сохранять (в отличие от # CIAN_REQUIRED_COOKIES / DOMCLICK_REQUIRED_COOKIES) — используется ТОЛЬКО в # has_auth_marker() для проверки "дамп вообще похож на авторизованную сессию". -# Замер #3192 сделан на ПОЛНОМ наборе из 44 cookies, какая именно cookie -# существенна для mainPhone-раскрытия — неизвестно, поэтому save_session хранит -# весь дамп целиком (за вычетом аналитики), а не только эти имена. НЕ сужай -# набор, который реально сохраняется, до этого списка. +# Замеры #3192 делались на ПОЛНОМ наборе из 44 cookies, за что отвечает каждая — +# неизвестно, поэтому save_session хранит весь дамп целиком (за вычетом аналитики), +# а не только эти имена. НЕ сужай набор, который реально сохраняется, до этого +# списка. YANDEX_CRITICAL_COOKIES: set[str] = { "Session_id", "sessionid2", @@ -77,12 +93,12 @@ ANALYTICS_COOKIE_PREFIXES: tuple[str, ...] = ("_ym_", "yabs-", "_yasc") def filter_analytics_cookies(cookies: list[dict[str, Any]]) -> list[dict[str, Any]]: """Drop только заведомую аналитику (_ym_*, yabs-*, _yasc*) — остальное как есть. - В отличие от Cian/DomClick здесь нет allowlist на сохранение: замер #3192 - сделан на ПОЛНОМ наборе из 44 cookies, значимость конкретной cookie для - mainPhone-раскрытия неизвестна. Сужать этот фильтр до allowlist известных - "критичных" имён — значит потерять часть замера у следующего инженера, - который решит его "оптимизировать". YANDEX_CRITICAL_COOKIES используется - только в has_auth_marker(), не здесь. + В отличие от Cian/DomClick здесь нет allowlist на сохранение: замеры #3192 + делались на ПОЛНОМ наборе из 44 cookies, значимость конкретной cookie + неизвестна. Сужать этот фильтр до allowlist известных "критичных" имён — + значит потерять часть замера у следующего инженера, который решит его + "оптимизировать". YANDEX_CRITICAL_COOKIES используется только в + has_auth_marker(), не здесь. """ return [c for c in cookies if not str(c.get("name", "")).startswith(ANALYTICS_COOKIE_PREFIXES)] diff --git a/tradein-mvp/backend/data/sql/274_yandex_session_cookies.sql b/tradein-mvp/backend/data/sql/274_yandex_session_cookies.sql index e3b56240..175a38b7 100644 --- a/tradein-mvp/backend/data/sql/274_yandex_session_cookies.sql +++ b/tradein-mvp/backend/data/sql/274_yandex_session_cookies.sql @@ -1,18 +1,20 @@ -- 274_yandex_session_cookies.sql -- Purpose: Encrypted storage for Yandex.Nedvizhimost browser session cookies. --- Замер на проде (сайдкар, две карточки, вперемежку с/без кук, 2026-08-28): --- author.phoneNumbers присутствует ТОЛЬКО в авторизованном режиме --- (0 без кук → 2 с куками, одинаково на обеих карточках), плюс три --- номера в author.phones сверху. Это контакт продавца с тегом канала --- и redirectId. --- NB: mainPhone НЕ собирать — он лежит в паспортном блоке рядом с --- login/passportHost/passportPhones, то есть это номер НАШЕЙ учётки, --- а не продавца. Первая версия этого замера приняла его за данные --- объявления; счётчики по конкретным полям это опровергли. --- encryptedPhones одинаков в обоих режимах (54) — зашифрованные токены --- отдаются всегда, различие не в них. -- 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.