SSO и федерация
SSO федерирует вход к IdP, но SP по-прежнему владеет проверкой на входе (подпись, issuer, audience, срок) и сносом сессии на выходе — а отзыв в федерации всегда лишь согласован в конечном счёте.
Клиент заводит тикет: подрядчик, которого он уволил в корпоративном каталоге неделю назад, всё ещё залогинен в вашем SaaS и скачивает отчёты. Вы смотрите в код — вход работает, кнопка «Войти через ваш IdP» зелёная, федерация настроена ровно так, как сказано в документации вендора. И всё же отключённый аккаунт гуляет по вашему приложению семь дней спустя. Ничто не «сломано» в смысле выброшенной ошибки: IdP аутентифицировал этого человека в день ноль, ваше приложение этому поверило, выпустило сессию — и забыло, что IdP существует. Эта дыра — между тем, как личность отзывают выше по потоку, и тем, как ваше приложение это замечает, — и есть та часть SSO, о которой туториал по интеграции никогда не упоминает, а разбор инцидента всегда находит. «Мы используем SSO» — это начало разговора об аутентификации, а не его конец.
К концу урока вы будете точно знать, что IdP делает за вас, чего он подчёркнуто не делает, какие четыре проверки SP по-прежнему обязан выполнять над каждым токеном, и почему отзыв в федерации согласован в конечном счёте, а не мгновенен.
Что федерация на самом деле делегирует — и что нет
Single sign-on позволяет пользователю аутентифицироваться один раз у центрального провайдера идентичности (IdP) — Okta, Entra ID, Google Workspace, собственный каталог клиента — и затем получать доступ ко многим поставщикам сервиса (SP): вашим приложениям. Федерация — это договорённость о доверии, которая делает IdP одной организации авторитетом, которому ваш SP будет верить. Два доминирующих протокола — SAML (XML-утверждения, с упором на корпоративный сектор) и OpenID Connect / OIDC (слой идентичности на основе JWT поверх OAuth 2.0). Разные форматы на проводе, одна форма: IdP аутентифицирует пользователя и передаёт вашему SP подписанное утверждение — SAML-assertion или OIDC ID token, — которое говорит «этот субъект аутентифицировался, вот его claims».
Вот что именно делегирует федерация: сам акт аутентификации пользователя. IdP выполняет проверку пароля, запрос MFA, церемонию passkey; вы никогда не видите учётные данные. Чего федерация не делегирует — это всё вокруг этого утверждения. SP по-прежнему обязан проверить, что утверждение подлинно и предназначено ему, решить, как долго доверять ему локально, и снести это локальное доверие, когда пользователя больше нет. Ментальная модель, ведущая к инцидентам, — «мы отдали аутентификацию IdP на аутсорс». Вы отдали проверку учётных данных. Проверка, срок жизни сессии и выход по-прежнему ваши.
Четыре проверки, которыми SP по-прежнему владеет над каждым токеном
Когда ID token приходит, «IdP его подписал» — необходимо, но и близко не достаточно. Senior относится к проверке как к четырём отдельным воротам, и пройти три из них — всё равно дыра.
- Подпись — подтвердить, что токен подписан тем IdP, которому вы доверяете, опубликованным ключом IdP (эндпоинт JWKS для OIDC, сертификат из метаданных SAML). Это отвечает на вопрос «произвёл ли это мой доверенный IdP?». Критично: зафиксируйте алгоритм вне канала на согласованном при настройке федерации (например, RS256) и никогда не доверяйте полю
algв собственном заголовке токена — классическая атака путаницы алгоритмов понижает RS256 до HS256 и подписывает публичным ключом IdP (который по определению публичен), используемым как секрет HMAC, и наивный верификатор это принимает. - Issuer (
iss) — подтвердить, что токен пришёл от конкретного IdP, с которым вы федерировались, а не просто от какого-то IdP, чей ключ вы случайно получили. Без этой проверки токен, выпущенный любым issuer, до которого вы можете достучаться, пропускается. - Audience (
aud) — подтвердить, что токен выпущен для вас. Claim audience привязывает токен к конкретному client ID SP. Это отвечает на другой вопрос, чем подпись: «было ли это произведено для меня?». В федерации с несколькими SP это огромная разница — подлинный, корректно подписанный токен, выпущенный для соседнего приложения SP-A, можно реплейнуть на SP-B, и если SP-B пропускает проверку audience, он принимает его как валидный вход. - Срок (
exp) — подтвердить, что токен ещё в окне валидности. Перехваченный токен без проверки срока работает вечно.
Подпись отвечает «кто подписал»; issuer, audience и срок — «от кого, для кого и всё ещё ли валиден?». Это четыре проверки, а не одна.
▸Частая ошибка
SAML-аналог путаницы алгоритмов — XML Signature Wrapping (XSW). Изъян — это баг порядка: код читает личность пользователя из «первого найденного <Subject>», а затем отдельно спрашивает библиотеку «валидна ли подпись документа?» — и оба шага успешны. Атакующий внедряет второй <Subject>, который читает приложение, тогда как валидная подпись по-прежнему покрывает исходный элемент. Проверить, что какая-то подпись валидна, не то же самое, что проверить, что она покрывает те самые байты, на которых вы действовали. Фикс — использовать проверенную SAML-библиотеку правильно, чтобы валидация подписи и извлечение личности были связаны с одним и тем же элементом — никогда не писать разбор XML вручную и никогда не читать личность ни из чего, кроме подписанного, проверенного узла.
Разрыв деактивации: федерация федерирует вход, а не выход
Это и есть сбой из Hook, и он структурный, а не опечатка. Утверждение IdP — это факт на момент времени: «этот субъект аутентифицировался в 09:14 в день ноль». Ваш SP проверяет его и выпускает локальную сессию — и эта сессия теперь ваша, отвязанная от IdP. Когда клиент отключает пользователя в день ноль, ничто не проталкивает это изменение в ваше хранилище сессий. Вход был pull (SP вытянул утверждение, когда пользователь пришёл); деактивация требует push (IdP сообщает SP «снеси это»), а базовый поток входа такого канала не имеет. Если ваша локальная сессия живёт 30 дней, отключённый пользователь продолжает работать до 30 дней, потому что ему не приходится возвращаться через IdP — который теперь отказал бы ему.
Чистого фикса, делающего внешний отзыв мгновенным, не существует, и притворяться обратным — ловушка. Вы не можете вызывать IdP клиента на каждом запросе — это добавляет задержку на каждую загрузку страницы и делает их сбой вашим сбоем. Вы не можете зеркалить весь их каталог локально — он дрейфует, и это снова делает вас владельцем деактивации, которую вы федерировали именно ради аутсорса. Поэтому senior-ход — ограничить окно и принять, что отзыв согласован в конечном счёте:
- Короткие сроки жизни сессий SP (минуты-несколько часов, не дни), чтобы деактивированного пользователя скоро отправило обратно через IdP и отказало. Это ограничивает рутинный устаревший доступ длиной сессии.
- Активный канал отзыва там, где это позволяет протокол: OIDC back-channel logout или SAML Single Logout, чтобы поддерживающий это IdP мог вызвать ваш logout-эндпоинт и немедленно снести сессии — рассматривается как best-effort, потому что не каждый IdP это реализует, а сети отказывают.
- Step-up перепроверка для чувствительных действий (платежи, смена ролей, экспорт данных): перепроверяйте IdP или требуйте свежий
auth_timeв момент действия, чтобы пути с наибольшим ущербом получили почти реальное время без вызова IdP на каждом чтении. - Прямо укажите остаточное окно в документах безопасности и контракте с клиентом: разрыв между отключением выше по потоку и истечением сессии SP ненулевой. Это владеемое, сообщённое свойство — а ужесточение срока сессии это ручка, которую клиент может попросить повернуть.
| Зона | Чем владеет IdP | Чем по-прежнему владеет SP |
|---|---|---|
| Проверка учётных данных | Пароль, MFA, церемония passkey | Ничем — полностью делегировано |
| Доверие к токену | Подписывает утверждение / ID token | Проверить sig, iss, aud, exp — зафиксировать алгоритм |
| Локальная сессия | Ничем после редиректа | Срок жизни, кука, где живёт истина |
| Отзыв | Отключает пользователя; может протолкнуть logout | Ограничить окно; провести back-channel logout |
| Сбой проверки подписи | — | путаница alg (JWT) / XML Signature Wrapping (SAML) |
Как senior рассуждает о любой интеграции «Войти через…»
Сделайте это постоянным вопросом для каждой настраиваемой федерации: когда эту личность отзывают выше по потоку, сколько проживёт моя сессия ниже по потоку — и владеемо ли и сообщено ли это окно? Затем прогоните фиксированный чек-лист на входе и на выходе: зафиксированные issuer и ключ, audience, ограниченный этим SP, подпись с зафиксированным алгоритмом, проверка срока, короткий срок жизни сессии, проведённый back-channel logout там, где IdP его поддерживает, и step-up перепроверка на чувствительных действиях. Считайте отзыв согласованным в конечном счёте по умолчанию. SP никогда не перестаёт владеть проверкой на входе и сносом на выходе — федерация переместила проверку учётных данных, а не вашу ответственность за этот шов.
Твой мультитенантный SaaS федерируется с собственным IdP каждого клиента. Клиенты ожидают, что отключение пользователя «быстро» останавливает доступ. Как ты ограничишь окно устаревшего доступа?
Подпись OIDC ID token проверяется против JWKS провайдера IdP, и срок не истёк. Почему этого всё ещё недостаточно, чтобы залогинить пользователя?
Почему нужно фиксировать ожидаемый алгоритм подписи вне канала, а не доверять полю alg в заголовке токена?
Расставь по порядку шаги корректного превращения OIDC-колбэка в доверенную локальную сессию:
- 1 Обменять authorization code на ID token на token-эндпоинте IdP
- 2 Проверить подпись против JWKS провайдера IdP с алгоритмом, зафиксированным вне канала (например, RS256)
- 3 Проверить, что iss — твой IdP, aud — client ID этого SP, а exp в будущем
- 4 Выпустить короткоживущую локальную сессию и провести back-channel logout, чтобы IdP мог её снести
- 01Федерация делегирует аутентификацию IdP. Назови точно, чем по-прежнему владеет SP и почему.
- 02Объясни разрыв деактивации и как ты его ограничиваешь, не делая свою доступность зависимой от IdP клиента.
Single sign-on позволяет пользователю аутентифицироваться один раз у центрального IdP и достичь многих SP, по SAML или OIDC — но федерация делегирует только проверку учётных данных. IdP выполняет церемонию пароля, MFA или passkey и передаёт вашему SP подписанное утверждение; всё остальное остаётся вашим. На входе SP владеет четырьмя отдельными воротами проверки: подпись (с алгоритмом, зафиксированным вне канала, никогда не доверяя собственному полю alg токена, иначе вы приглашаете путаницу алгоритмов RS256→HS256; SAML-аналог — XML Signature Wrapping), issuer, audience (чтобы токен, выпущенный для соседнего SP, не реплейнулся) и срок — потому что «IdP это подписал» отвечает, кто подписал, а не от-кого-для-кого-и-всё-ещё-ли-валиден. На выходе SP владеет сносом, и именно здесь SSO тихо ломается: федерация федерирует вход (pull), но не выход (push), поэтому, когда личность отзывают выше по потоку, ничто не доходит до вашего хранилища сессий, и долгоживущая локальная сессия держит отключённого пользователя внутри вашего приложения. Вы не можете сделать внешний отзыв мгновенным, поэтому вы ограничиваете окно — короткие сроки жизни сессий, OIDC back-channel logout или SAML Single Logout как best-effort, step-up перепроверка на чувствительных действиях — и заявляете остаточный разрыв, а не делаете вид, что он нулевой. Постоянный вопрос для каждой интеграции «Войти через…»: когда эту личность отзывают выше по потоку, сколько проживёт моя сессия ниже по потоку, и владеемо ли и сообщено ли это окно?
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.