Saml: фрактал плохого дизайна
Знаете, что меня зацепило в этой статье: 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 ──▶ проверка ──▶ доступ
Подписи: хрупкие по своей природе
В SAML подпись встроена внутрь данных, которые она же и защищает. Это так называемая enveloped signature. В отличие от форматов, где подпись отделена от полезной нагрузки, здесь приходится добиваться байт-в-байт идентичности сложной XML-структуры.
На практике это означает, что система должна безупречно обработать и сам XML, и его каноническое представление. Малейшее расхождение в парсинге, порядке атрибутов или обработке комментариев способно разрушить проверку подписи или, наоборот, создать иллюзию корректности там, где её нет.
Каноникализация: источник бесконечных багов
Каноникализация должна привести XML к единому виду перед вычислением хеша. Звучит разумно, но на деле это почти бесконечный источник пограничных случаев. Разные библиотеки, версии парсеров и настройки обработки XML могут по-разному трактовать один и тот же документ.
Из-за этого IdP и SP иногда видят не один и тот же документ, а две слегка разные интерпретации. Для безопасности это катастрофа: протокол опирается на предположение, что обе стороны согласны по поводу структуры и содержания assertion, а это предположение оказывается хрупким.
XML Signature Wrapping
──────────────────────
┌───────────────┐
│ valid token │
└───────┬───────┘
│
▼
┌────────────────────┐
│ обёртка / подмена │
├────────────────────┤
│ подпись остаётся │
│ валидной │
└─────────┬──────────┘
▼
неверные данные
Атаки, которые не должны работать, но работают
Один из самых известных классов атак на SAML — XML Signature Wrapping. Суть в том, чтобы взять валидный assertion, изменить структуру документа и заставить приложение проверить подпись на одном фрагменте, а использовать в логике — другой.
Это особенно опасно, потому что внешне всё выглядит корректно: подпись действительна, XML валиден, аутентификация прошла. Но фактически приложение принимает подменённые данные.
Именно поэтому исследователи неоднократно показывали, как автоматизировать такие атаки, а уязвимости находят до сих пор. Проблема не в том, что разработчики недостаточно внимательны. Проблема в том, что сама модель допускает подобный разрыв между проверкой и использованием.
Почему спецификация сама по себе тяжёлая
SAML 2.0 — это компромисс нескольких разных предшествующих решений, сведённых в один стандарт. В результате получилась сложная спецификация, из которой в реальных системах используется лишь небольшая часть. Остальное всё равно нужно поддерживать, парсить и валидировать — а значит, тащить за собой лишнюю сложность.
Чем больше необязательных веток поведения, тем больше мест, где реализации начинают расходиться. А чем больше расхождений, тем выше шанс, что одна сторона будет считать документ безопасным, а другая — нет.
Что это значит для практики
Если вы поддерживаете систему на SAML, вам нужно относиться к XML-парсерам и библиотекам подписи как к критически важной части поверхности атаки. Обновления, CVE-мониторинг и жёсткое тестирование здесь не формальность, а необходимость.
Если вы проектируете новую систему, SAML стоит рассматривать очень осторожно. В большинстве случаев современный стек на базе OIDC проще, прозрачнее и заметно легче в проверке. Это не делает его идеальным, но делает жизнь инженеров гораздо менее хрупкой.
Главный вывод не в том, что SAML «плохой» в бытовом смысле. Вывод в том, что это протокол, в котором безопасность слишком сильно зависит от идеально согласованных деталей. А в реальном мире такие системы почти никогда не бывают идеальными.
Ссылки
- SAML: A fractal of bad design — Trail of Bits Blog — статья, ставшая основой для разбора
Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.