Saml: фрактал плохого дизайна

23.09.2026 · 5 мин

Знаете, что меня зацепило в этой статье: SAML часто ломается не потому, что кто-то где-то ошибся, а потому, что сам протокол почти провоцирует на ошибки.

Зачем нужен saml и почему его до сих пор используют

SAML (Security Assertion Markup Language) — это протокол аутентификации для единого входа, когда пользователь один раз логинится и получает доступ к нескольким сервисам. В начале 2000-х, когда веб-приложений стало резко больше, это решало очень практичную проблему: слишком много паролей, слишком много разных систем, слишком много ручного управления доступом.

Университеты и крупные поставщики быстро подхватили идею. Появились CAS, Shibboleth, ADFS, а позже и целая индустрия вокруг SSO и федеративной аутентификации. SAML стал стандартом де-факто в enterprise-среде — но вместе с масштабом пришла и сложность.

Фрактал плохого дизайна

Главная мысль здесь простая: у SAML нет одной «большой» проблемы. Его слабость повторяется на каждом уровне — в формате данных, в проверке подписей, в каноникализации XML и в самой модели доверия между IdP и SP.

Именно поэтому баги в SAML так упорно возвращаются. Исправляешь один класс уязвимостей — и тут же находишь другой, потому что источником проблем является не только код, но и архитектура.

Xml: не тот инструмент для этой работы

SAML построен на XML, а XML — это сложный, многословный и хрупкий формат. У него много уровней абстракции: теги, атрибуты, пространства имён, схемы, DTD, сущности, комментарии, CDATA. Всё это увеличивает поверхность атаки и усложняет парсинг.

Вместо простого и предсказуемого формата разработчик получает целый набор потенциальных проблем: XXE, инъекции в XPath и XQuery, расхождения между парсерами и неожиданные эффекты при сериализации. И прежде чем система вообще дойдёт до проверки логики аутентификации, ей нужно безопасно пережить весь этот слой.

Подпись и каноникализация
─────────────────────────
IdP ──▶ XML ──▶ подпись ──▶ Assertion
 │                 │
 │                 └──▶ каноникализация
 ▼
SP ──▶ XML ──▶ проверка ──▶ доступ
Если IdP и SP по-разному интерпретируют XML, подпись может сломаться или, что хуже, подтвердить не тот объект.

Подписи: хрупкие по своей природе

В SAML подпись встроена внутрь данных, которые она же и защищает. Это так называемая enveloped signature. В отличие от форматов, где подпись отделена от полезной нагрузки, здесь приходится добиваться байт-в-байт идентичности сложной XML-структуры.

На практике это означает, что система должна безупречно обработать и сам XML, и его каноническое представление. Малейшее расхождение в парсинге, порядке атрибутов или обработке комментариев способно разрушить проверку подписи или, наоборот, создать иллюзию корректности там, где её нет.

Каноникализация: источник бесконечных багов

Каноникализация должна привести XML к единому виду перед вычислением хеша. Звучит разумно, но на деле это почти бесконечный источник пограничных случаев. Разные библиотеки, версии парсеров и настройки обработки XML могут по-разному трактовать один и тот же документ.

Из-за этого IdP и SP иногда видят не один и тот же документ, а две слегка разные интерпретации. Для безопасности это катастрофа: протокол опирается на предположение, что обе стороны согласны по поводу структуры и содержания assertion, а это предположение оказывается хрупким.

XML Signature Wrapping
──────────────────────
┌───────────────┐
│  valid token   │
└───────┬───────┘
        │
        ▼
┌────────────────────┐
│ обёртка / подмена   │
├────────────────────┤
│ подпись остаётся    │
│ валидной            │
└─────────┬──────────┘
          ▼
     неверные данные
Атака XML Signature Wrapping использует разрыв между тем, что подписано, и тем, что реально обрабатывает приложение.

Атаки, которые не должны работать, но работают

Один из самых известных классов атак на SAML — XML Signature Wrapping. Суть в том, чтобы взять валидный assertion, изменить структуру документа и заставить приложение проверить подпись на одном фрагменте, а использовать в логике — другой.

Это особенно опасно, потому что внешне всё выглядит корректно: подпись действительна, XML валиден, аутентификация прошла. Но фактически приложение принимает подменённые данные.

Именно поэтому исследователи неоднократно показывали, как автоматизировать такие атаки, а уязвимости находят до сих пор. Проблема не в том, что разработчики недостаточно внимательны. Проблема в том, что сама модель допускает подобный разрыв между проверкой и использованием.

Почему спецификация сама по себе тяжёлая

SAML 2.0 — это компромисс нескольких разных предшествующих решений, сведённых в один стандарт. В результате получилась сложная спецификация, из которой в реальных системах используется лишь небольшая часть. Остальное всё равно нужно поддерживать, парсить и валидировать — а значит, тащить за собой лишнюю сложность.

Чем больше необязательных веток поведения, тем больше мест, где реализации начинают расходиться. А чем больше расхождений, тем выше шанс, что одна сторона будет считать документ безопасным, а другая — нет.

Что это значит для практики

Если вы поддерживаете систему на SAML, вам нужно относиться к XML-парсерам и библиотекам подписи как к критически важной части поверхности атаки. Обновления, CVE-мониторинг и жёсткое тестирование здесь не формальность, а необходимость.

Если вы проектируете новую систему, SAML стоит рассматривать очень осторожно. В большинстве случаев современный стек на базе OIDC проще, прозрачнее и заметно легче в проверке. Это не делает его идеальным, но делает жизнь инженеров гораздо менее хрупкой.

Главный вывод не в том, что SAML «плохой» в бытовом смысле. Вывод в том, что это протокол, в котором безопасность слишком сильно зависит от идеально согласованных деталей. А в реальном мире такие системы почти никогда не бывают идеальными.

Ссылки

Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.