docs(tradein/scrapers): в репозитории лежало опровергнутое обоснование хранилища сессий Яндекса
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
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
#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
This commit is contained in:
parent
fcc6c6576a
commit
c995af73c2
2 changed files with 45 additions and 27 deletions
|
|
@ -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)]
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue