Compare commits

..

No commits in common. "ede1243ce6485e7d72009c49c52bffcaccd1db19" and "fcc6c6576a6c2ebed1bec795a218e41ddf5eb057" have entirely different histories.

2 changed files with 27 additions and 45 deletions

View file

@ -1,25 +1,9 @@
"""Yandex.Nedvizhimost session cookie management — load/save/invalidate encrypted cookies. """Yandex.Nedvizhimost session cookie management — load/save/invalidate encrypted cookies.
ОБОСНОВАНИЕ ЭТОГО МОДУЛЯ ОПРОВЕРГНУТО (#3192, замер 2026-08-28, разбор в тикете). Замер на проде (#3192, сайдкар, две карточки, режимы вперемежку, 2026-08-28):
Модуль строился под гипотезу «авторизованная сессия раскрывает контакты продавца». авторизованная сессия Яндекс.Недвижимости отдаёт ключ `mainPhone` и на 11 больше
Повторный замер на ПРОД-транспорте (curl_cffi + прокси, тот же путь, что у уникальных телефонов продавца (90101 / 89100), чем анонимный запрос той же
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 Cookies хранятся зашифрованно (pgp_sym_encrypt) в yandex_session_cookies
зеркалит app.services.cian_session / app.services.domclick_session, но: зеркалит app.services.cian_session / app.services.domclick_session, но:
@ -31,8 +15,8 @@ Cookies хранятся зашифрованно (pgp_sym_encrypt) в yandex_se
browser context (следующая часть #3192) требует domain/path, не только value; browser context (следующая часть #3192) требует domain/path, не только value;
- НЕТ allowlist-фильтра на сохранение (см. filter_analytics_cookies docstring) - НЕТ allowlist-фильтра на сохранение (см. filter_analytics_cookies docstring)
Cian/DomClick фильтруют payload по списку известных cookie-имён перед Cian/DomClick фильтруют payload по списку известных cookie-имён перед
сохранением, здесь так делать нельзя: замеры делались на ПОЛНОМ наборе из сохранением, здесь так делать нельзя: замер выше сделан на ПОЛНОМ наборе из
44 cookies, и какая из них за что отвечает неизвестно. 44 cookies, и какая из них существенна для mainPhone-раскрытия неизвестно.
MVP: без verify_session (аналогично domclick_session) верификация Yandex-сессии MVP: без verify_session (аналогично domclick_session) верификация Yandex-сессии
требует реального browser-фетча, не простого curl_cffi-запроса; вне рамок этой требует реального browser-фетча, не простого curl_cffi-запроса; вне рамок этой
@ -64,10 +48,10 @@ COOKIE_EXPIRY_WARN_DAYS = 5
# ВАЖНО: это НЕ allowlist для фильтрации того, что сохранять (в отличие от # ВАЖНО: это НЕ allowlist для фильтрации того, что сохранять (в отличие от
# CIAN_REQUIRED_COOKIES / DOMCLICK_REQUIRED_COOKIES) — используется ТОЛЬКО в # CIAN_REQUIRED_COOKIES / DOMCLICK_REQUIRED_COOKIES) — используется ТОЛЬКО в
# has_auth_marker() для проверки "дамп вообще похож на авторизованную сессию". # has_auth_marker() для проверки "дамп вообще похож на авторизованную сессию".
# Замеры #3192 делались на ПОЛНОМ наборе из 44 cookies, за что отвечает каждая — # Замер #3192 сделан на ПОЛНОМ наборе из 44 cookies, какая именно cookie
# неизвестно, поэтому save_session хранит весь дамп целиком (за вычетом аналитики), # существенна для mainPhone-раскрытия — неизвестно, поэтому save_session хранит
# а не только эти имена. НЕ сужай набор, который реально сохраняется, до этого # весь дамп целиком (за вычетом аналитики), а не только эти имена. НЕ сужай
# списка. # набор, который реально сохраняется, до этого списка.
YANDEX_CRITICAL_COOKIES: set[str] = { YANDEX_CRITICAL_COOKIES: set[str] = {
"Session_id", "Session_id",
"sessionid2", "sessionid2",
@ -93,12 +77,12 @@ ANALYTICS_COOKIE_PREFIXES: tuple[str, ...] = ("_ym_", "yabs-", "_yasc")
def filter_analytics_cookies(cookies: list[dict[str, Any]]) -> list[dict[str, Any]]: def filter_analytics_cookies(cookies: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Drop только заведомую аналитику (_ym_*, yabs-*, _yasc*) — остальное как есть. """Drop только заведомую аналитику (_ym_*, yabs-*, _yasc*) — остальное как есть.
В отличие от Cian/DomClick здесь нет allowlist на сохранение: замеры #3192 В отличие от Cian/DomClick здесь нет allowlist на сохранение: замер #3192
делались на ПОЛНОМ наборе из 44 cookies, значимость конкретной cookie сделан на ПОЛНОМ наборе из 44 cookies, значимость конкретной cookie для
неизвестна. Сужать этот фильтр до allowlist известных "критичных" имён mainPhone-раскрытия неизвестна. Сужать этот фильтр до allowlist известных
значит потерять часть замера у следующего инженера, который решит его "критичных" имён значит потерять часть замера у следующего инженера,
"оптимизировать". YANDEX_CRITICAL_COOKIES используется только в который решит его "оптимизировать". YANDEX_CRITICAL_COOKIES используется
has_auth_marker(), не здесь. только в has_auth_marker(), не здесь.
""" """
return [c for c in cookies if not str(c.get("name", "")).startswith(ANALYTICS_COOKIE_PREFIXES)] return [c for c in cookies if not str(c.get("name", "")).startswith(ANALYTICS_COOKIE_PREFIXES)]

View file

@ -1,20 +1,18 @@
-- 274_yandex_session_cookies.sql -- 274_yandex_session_cookies.sql
-- Purpose: Encrypted storage for Yandex.Nedvizhimost browser session cookies. -- 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 (зеркалит -- Uses pgcrypto pgp_sym_encrypt for AES encryption at rest (зеркалит
-- 027_cian_session_cookies.sql / 174_domclick_session_cookies.sql). -- 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: -- Dependencies:
-- - pgcrypto extension (installed here via CREATE EXTENSION IF NOT EXISTS) -- - pgcrypto extension (installed here via CREATE EXTENSION IF NOT EXISTS)
-- Deploy order: Apply after 273. -- Deploy order: Apply after 273.