Документ описывает только то, что подтверждено сохранёнными HAR, JavaScript- бандлами, декомпилированным актуальным Android-клиентом и безопасными live- пробами. Значения cookies, финансовые ответы и другие секреты здесь не приводятся.
Для актуальных web-endpoints балансов и операций не найден OAuth-подобный
access_token и не используется Authorization: Bearer ....
Клиентская схема — cookie-backed web session:
post-login cookie bundle
|
+--> UFS / PLSBOL: withCredentials + Cookie
|
+--> CBRO sync: POST /web/auth/api/v2/token + Cookie
| |
| +--> settings/session marker, не access_token
|
+--> warm-up: POST /api/warmUpSession + Cookie
UFS-TOKEN — наиболее очевидный token-like элемент, но это Secure/HttpOnly
cookie, а не Bearer-токен. Код Android-клиента выделяет минимальный кандидат из
трёх значений: UFS-SESSION, UFS-TOKEN, SWJSESSIONID. Live web-тест сузил
этот набор до UFS-SESSION и UFS-TOKEN.
В старом мобильном клиенте из MEJIOMAH17/sberbank-api слово token означает
промежуточный результат входа:
POST /CSAMAPI/login.doвозвращаетresponse/loginData/token.- Значение сразу передаётся form-полем
tokenвPOST /mobile9/postCSALogin.do. - Результатом обмена становится
JSESSIONIDизSet-Cookie. - Последующие legacy-запросы идут уже в cookie-сессии.
Доказательства:
work/MEJIOMAH17-sberbank-api/src/main/kotlin/ru/mekosichkin/sberbank/api/SberbankLogining.kt:18-21— извлечениеresponse/loginData/tokenи вызовpostCSALogin;- тот же файл
:57-87— token передаётся в body, результат берётся изSet-Cookie: JSESSIONID; work/Personal-Finance-App/src/main/java/financeapp/bankConnection/sberbank/api/calls/SberbankApi.java:35-47— отдельные login, postCSALogin и private endpoints.
Это prelogin exchange token, а не долго живущий access token. Он не должен
попадать в SessionBundle, не должен сохраняться после обмена и не доказан как
часть актуального web-flow. Старый код использует мобильные endpoints, версии и
порт другого поколения; их текущая работоспособность не проверялась.
Сохранённый web capture начинается уже после входа. Auth shell делает
GET {ufsHost}/auth/web с credentials: include, а business-клиенты включают
withCredentials=true. Ни shell, ни UFS-interceptor не извлекают access token и
не добавляют Bearer header.
Доказательства:
work/har-js/pl-res.online.sberbank.ru/csa.startup/1.2.1/index.js:356—credentials:"include";- тот же файл
:2338—/auth/web; work/har-js/pl-res.online.sberbank.ru/webpage.provider.bootstrap/1.23.0/bootstrap-plugins.js:9083—e.withCredentials=!0;- тот же файл
:22474— добавляетсяX-Requested-With, но не auth header; - products
region.products.cards/3.4.1/index.js:1522263—/main-screen/rest/v2/m1/web/section/meta; - operations
region.operations/3.9.0/index.js:1072554—/uoh-bh/v1/operations/list.
Полный скан 50 сохранённых .js/.map файлов (20 772 421 байт) дал ноль
совпадений для access_token, refresh_token и Bearer. Встречи слова
Authorization относятся к generic Axios Basic Auth, парсеру заголовков и
статусу банковской операции AUTHORIZATION, а не к авторизации UFS.
Актуальный ESA bundle содержит отдельный PIN-flow, который теперь реализован в
AsyncPinAuth. Он не выпускает access token и не работает только по одному
PIN: стартовое состояние — полный cookie jar уже запомненного браузера.
Последовательность фронтенда:
GET /CSAFront/index.doи безопасный разборwindow.configбезeval.- Из config берутся динамические
baseApiUrl,processId,pinConfig.srp_N,pinConfig.srp_gи длина PIN. POST {baseApiUrl}/api/v1/pin/beginпередаёт новыйsrp_Aи строкуdeviceprint, полученную фронтендом изwindow.bfd.getData().- Клиент вычисляет нестандартный SRP/SHA-512
srp_M; PIN не включает login. POST .../pin/logon, затем обязательная constant-time проверкаsrp_R.- Ответ либо требует
/pin/otp/confirm, либо содержит redirect. - При
isUfsRedirectMethodPostEnabled=trueredirect выполняется пустым POST сX-Seamless-Web: true; HTML/auth/webотдаёт динамический UFS API host. - После
/api/front/readyуспех признаётся только при наличииUFS-SESSIONиUFS-TOKEN; для старого config сохранён GET fallback.
Каждый POST получает новый frontend-совместимый Rq-Uid, постоянный
Process-Id текущего процесса и вращаемый X-CSRF-Token. Captcha и OTP retry
реализованы теми же endpoints. Автоповторов POST нет: network failure после
приёма запроса сервером нельзя безопасно отличить от failure до отправки.
Полный HAR-цикл и live-вход 2026-07-14 подтвердили относительный auth base
CSAFront, remembered PIN длиной 5, 2048-bit SRP group, seamless POST redirect
и динамический web-node из HTML /auth/web.
Полный capture нового браузера содержит отдельную cold-browser цепочку, теперь
реализованную в AsyncPrimaryAuth:
GET /CSAFront/index.do:authTypeByCookie=start, SRPN/g, новыйprocessId, PIN ещё отсутствует.- Первый form POST
/CSAFront/authMainJson.do:operation=button.begin, login,storeLogin,deviceprint,srp_A. - Ответ даёт
token,srp_s,srp_B; второй form POST отправляетsrp_Mи оба token-поля Struts/front-end. - После проверки
srp_Rсервер возвращаетNEED_CONFIRM; третий form POST отправляет SMS-код и новый token. pinInfo.publicKeyиспользуется для RSA-OAEP/SHA-1/MGF1-SHA-1 шифрования нового PIN. Открытый PIN в запрос не попадает.- JSON POST
/api/v1/pin/create, затем/api/v1/authс новымRq-Uid,Process-Idи текущим CSRF. - Пустой seamless POST на
/auth/webвыдаёт динамические UFS hosts и cookies;/api/front/readyзавершает вход.
Три form-body, заголовки, token transition, CSRF/Rq-Uid и redirect проверяются точным синтетическим контрактным тестом. Реальный 2048-bit public key из HAR успешно разобран и даёт ожидаемый 256-byte RSA ciphertext. Это подтверждает воспроизведение capture, но не означает, что cold-browser вход был повторно запущен live после реализации.
Короткая UFS-сессия и remembered-browser state — разные уровни. Версия 3
SessionBundle хранит полный cookie jar, browser headers, raw deviceprint,
опциональный opaque antifraud_deviceprint для write-запросов и
последние UFS hosts. В исследованном snapshot sb_user имел срок около 45 дней,
а sso_private — около 24 часов; это наблюдаемые сроки JWT/cookie, не публичный
refresh-token контракт.
AsyncSberClient.from_pin_profile() делает следующее:
- Использует текущие
UFS-SESSION/UFS-TOKEN, пока сервер их принимает. - На первом redirect/
401/403сериализует релогин одним async-lock. - Запускает
AsyncPinAuthна полном remembered jar и том же deviceprint. - После успеха атомарно сохраняет весь вращённый профиль с mode
0600. - Заменяет transport и повторяет исходный read-only запрос ровно один раз.
Второй отказ, CAPTCHA/SMS challenge или отклонённый remembered state наружу передаются как typed error. Бесконечных повторов и попыток обхода серверных ограничений нет.
По метаданным экспортированной сессии это cookie для
.online.sberbank.ru, path /, Secure и HttpOnly.
Что доказано:
- браузер отправляет его автоматически как часть подходящего cookie jar;
- JavaScript не может прочитать его из-за HttpOnly;
- UFS business layer использует cookie credentials.
Что не доказано:
- внутренний формат и семантика значения;
- возможность использовать значение в
Authorization; - переносимость значения между независимыми входами и устройствами.
Поэтому SDK может принимать поле ufs_token для удобства, но обязан установить
его как cookie с правильными domain/path/flags. Отправлять его как Bearer нельзя.
UFS-SESSION также имеет domain .online.sberbank.ru, path /, Secure и
HttpOnly. В browser session одновременно присутствуют SWJSESSIONID,
JSESSIONID, SSO и служебные cookies. Frontend не выбирает из них «главную»:
withCredentials отправляет весь подходящий набор, а решение принимает сервер.
Android-клиент даёт более сильную границу, чем web JavaScript:
eo0/C3288a.java:22-28,46— модельSbolAuthDataсостоит изufsBlockUrl,ufsSession,ufsToken,swjSessionId;Mo0/C7151a.java:24-37— значения берутся из token source какUFS-SESSION,UFS-TOKEN,SWJSESSIONID;JP0/C26069e.java:39-44— auth-фильтр содержит ровно регулярное выражениеUFS-SESSION|UFS-TOKEN|SWJSESSIONID;Ly6/h.java:189-224— ровно эти три значения устанавливаются как Secure и HttpOnly cookies на UFS host.
Это Android-модель, а не обязательный web minimum. Контролируемый web
cookie-elimination показал, что SWJSESSIONID, SSO, timestamp, TS/WAF и
telemetry cookies для products/operations не нужны.
После свежего входа текущий browser API host оказался
web-standin2.online.sberbank.ru; старый HAR содержал уже неактуальные
web-node2/web2 и мобильный UA. С текущим host и штатным curl_cffi
Chrome-профилем полный control вернул 200/success=true для products и
operations. Сохранённый User-Agent не потребовался.
После реализации profile lifecycle отдельная live-проверка 2026-07-14 прошла
через curl_cffi без браузера: remembered PIN flow обновил полный профиль,
products вернул карты/счета, а scoped operations — пять последних операций.
Значения финансовых данных в репозиторий не записывались.
Результат последовательного leave-one-out с control после каждой пробы:
UFS-SESSION + UFS-TOKEN + SWJSESSIONID— оба API200;- без
UFS-SESSION— products403, следующий полный control200; - без
UFS-TOKEN— products403, следующий полный control200; - без
SWJSESSIONID— products и operations200, следующий control200.
Наблюдаемый web minimum для этой сессии: UFS-SESSION и UFS-TOKEN. Оба
обязательны, вместе достаточны. Автоматический warm-up удалён из business-flow:
на старом web2 он дал 403, на текущем UFS host — 404, тогда как оба
business endpoint стабильно отвечали 200 без warm-up.
Business host назначается сервером динамически. Первый HTML
https://online.sberbank.ru/app/main передаёт startup-loader значение
ufsHost; loader с credentials: include получает {ufsHost}/main. Уже этот
HTML содержит options.config["ufs.block.root.url"], а UFS interceptor читает
ключ при сборке абсолютного URL каждого business-запроса. В обоих сохранённых
HAR значение было web-node2.online.sberbank.ru; текущий live runtime выдал
web-standin2.online.sberbank.ru. В статическом bootstrap JS и Android APK
актуальный web host не зафиксирован.
Поэтому browserless SDK не может вычислить host из двух UFS cookies офлайн.
HAR-import извлекает request host и ufs.block.root.url. Конструктор из
credentials теперь при первом business-запросе повторяет frontend bootstrap:
GET /app/main -> ufsHost -> GET {ufsHost}/main -> ufs.block.root.url.
Оба GET используют один cookie jar; discovery защищён lock и выполняется один
раз на экземпляр клиента. Redirect/401/403 означают истёкшую сессию, а
missing/conflicting/external host отклоняется без fallback. Явные api_base и
web_base остаются только как override для диагностики и тестов.
Название endpoint не означает выдачу OAuth access token.
Фактический flow:
- CBRO сравнивает cookie session timestamp с marker в
sessionStorage. - При mismatch выполняет
POST {UfsCBroHost}/web/auth/api/v2/tokenс body{"platform":"web"}и cookie credentials. - Код читает только optional
data.settings[]. - Затем сохраняет timestamp marker и продолжает CBRO-запрос.
- HTTP 404 этого sync-вызова явно допускается.
Доказательства в bootstrap-plugins.js:
- byte
9179—data:{platform:"web"}; - byte
10280—/web/auth/api/v2/token; - byte
10484—session.cookie.name; - byte
10650— чтение только.data.settings; - byte
11427— recovery через/sm-uko/v3/web/session/notification.
В vendor constants webpage.provider.bootstrap/1.23.0/index.js:12960 заданы
статусы 400, 401, 404; wrapper трактует 404 token-sync как допустимый
ответ. Значение response header ufs-session клиентским JS не читается: такой
literal отсутствует во всех сохранённых assets.
Следовательно, CBRO sync нельзя экспортировать как access token и нельзя использовать вместо UFS cookie session.
В текущем Chrome также присутствовала HttpOnly cookie JWT-Authorization, но
её domain — .mc-cbro.online.sberbank.ru. По RFC scope она не отправляется на
web-node2.online.sberbank.ru или web2.online.sberbank.ru, поэтому не входит
в credentials для products/operations/warm-up. Значение не декодировалось и не
логировалось; для отдельного CBRO SDK потребовался бы другой контракт.
Runtime session.cookie.name указывает на SBTSBOL_SESSION_TIMESTAMP. Это
доступный JavaScript marker поколения сессии, а не секрет авторизации.
Основной shell сравнивает его с sessionStorage["FRONT_MONITORING"]. При смене
вызывает /front/ready, затем сохраняет новый marker. CBRO использует тот же
принцип для cbroSessionTimestampName.
Доказательства в work/har-js/pl-res.online.sberbank.ru/index/3.8.1/index.js:
- byte
209559—/front/ready; - byte
254783—FRONT_MONITORING; - byte
254813—session.cookie.name; - byte
262002—SBTSBOL_SESSION_TIMESTAMP.
Timestamp полезен для определения необходимости синхронизации, но сам по себе не авторизует products/operations.
Session renew plugin отправляет POST /api/warmUpSession через PLSBOL с cookie
credentials. В исследованном runtime activity debounce равен 60 секундам, а
frontend lifetime — 15 минут. После успеха frontend переставляет локальный
таймер окончания сессии.
Доказательства в bootstrap-plugins.js:
- byte
29274—/api/warmUpSession; - byte
29322—session.front.lifetime; - byte
29568—session.autorenew.debounce; - byte
29635—session.autorenew.events.
Warm-up продлевает уже существующую cookie-сессию. Это не refresh_token и не
источник нового SDK-visible token. Любые Set-Cookie обновления ответа должны
атомарно применяться к тому же jar.
Публичная модель называется SberCredentials, а не AccessToken:
credentials = SberCredentials(
ufs_session="...",
ufs_token="...",
)
async with AsyncSberClient.from_credentials(credentials) as client:
products = await client.products.get()
rotated = client.session.credentials()Для файлового хранения передайте session_path: SDK будет атомарно сохранять
ротированные credentials после каждого успешного business/warm-up запроса.
Требования к контракту:
- принимать только opaque auth values, без Netscape/
Cookieстроки; - назначать им domain/path/Secure/HttpOnly внутри SDK;
- считать значения opaque: не декодировать и не пытаться определить JWT;
- не смешивать transport identity (UA/Client Hints/TLS) с auth secrets;
- применять server
Set-Cookieк живому jar после каждого ответа; - возвращать ротированные credentials в память и, при заданном
session_path, автоматически сохранять их; - не включать в bundle legacy prelogin token, Investor
authCode, chat token или response headerufs-session.
SessionBundle остаётся внутренней/миграционной полной моделью cookie jar.
SberCredentials.from_bundle() позволяет перейти со старого экспорта на два
поля. Результат следует повторить после следующего независимого входа, прежде
чем считать его универсальной гарантией для всех устройств.
Подтверждено:
- cookie-based transport текущего web read-only flow;
- отсутствие Bearer/access-token логики в сохранённых assets;
- назначение CBRO sync, timestamp marker и warm-up на уровне frontend-кода;
- наличие token-like HttpOnly cookies в post-login browser session;
- тройка
UFS-SESSION/UFS-TOKEN/SWJSESSIONIDкак отдельная auth-модель в Android-клиенте; - web minimum из
UFS-SESSIONиUFS-TOKENв свежей сессии; - успешная работа без сохранённого User-Agent и без warm-up.
- точные PIN/SRP формулы, endpoints, body builders, CSRF/Rq-Uid interceptors, captcha, SMS и redirect flow актуального ESA bundle;
- безопасный live bootstrap текущего
window.configбез отправки PIN. - успешный end-to-end PIN-вход и совпадение его сетевой формы с полным HAR.
- успешный автоматический PIN-релогин, атомарное сохранение полного профиля и последующий live products/scoped-operations read-only запрос;
- точная cold-browser state machine и RSA public-key shape из полного HAR.
Не подтверждено:
- повторный live-запуск cold-browser login/password и создание нового PIN после реализации (сама цепочка подтверждена полным HAR и контрактным тестом);
- какой сервер выпускает и обновляет
UFS-TOKEN/UFS-SESSION; - повторяемость двух-cookie minimum после независимого нового входа;
- переносимость bundle между IP, TLS fingerprint, device identity или browser profile;
- текущая работоспособность legacy mobile prelogin flow;
- требования write/payment endpoints и antifraud.
SDK не заявляет выпуск access token: успешный PIN-flow возвращает полный
ротированный cookie SessionBundle. Истёкшая UFS-сессия восстанавливается по
PIN только пока сервер принимает remembered-browser jar; SMS/captcha всё равно
могут потребовать участие пользователя.
- Не коммитить HAR, cookie jar,
SberCredentialsили экспортированныйSessionBundle. - На диске использовать Keychain/secret store; временный файл — только
0600. - Не передавать секреты через CLI arguments, URL query или environment dump.
- Редактировать
repr, exceptions, traces и HTTP debug logs: показывать только имя cookie, никогда значение. - Запретить сериализацию
SecretStrобычнымjson.dumpsбез явного opt-in. - Не копировать bundle между пользователями и окружениями.
- При
401,403или redirect один PIN-релогин и один retry разрешены только для read-запроса. Mutation POST не переигрывается: сначала выполняется безопасный read-preflight, а неопределённый результат проверяется по продуктам/истории. - После logout или замены сессии удалять старый bundle и очищать jar в памяти.
- Не пытаться продлевать сессию бесконечно и не обходить серверные ограничения.
Полный индекс доказательств и byte offsets находится в
work/auth_token_deep_report.md.