fix(tradein/browser): в репозитории лежало опровергнутое обоснование хранилища сессий Яндекса (#3200)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m53s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m34s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s

This commit is contained in:
bot-backend 2026-08-28 19:58:51 +00:00
commit ede1243ce6
2 changed files with 45 additions and 27 deletions

View file

@ -1,9 +1,25 @@
"""Yandex.Nedvizhimost session cookie management — load/save/invalidate encrypted cookies.
Замер на проде (#3192, сайдкар, две карточки, режимы вперемежку, 2026-08-28):
авторизованная сессия Яндекс.Недвижимости отдаёт ключ `mainPhone` и на 11 больше
уникальных телефонов продавца (90101 / 89100), чем анонимный запрос той же
карточки контакты продавца физически недоступны без валидной сессии.
ОБОСНОВАНИЕ ЭТОГО МОДУЛЯ ОПРОВЕРГНУТО (#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)]

View file

@ -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.