SAML больше двадцати лет обеспечивает единый вход (SSO) в корпоративной и академической среде. Но протокол прогибается под тяжестью собственной сложности, и ему пора на пенсию. Разбираемся, как SAML появился, почему его раз за разом ломают исследователи безопасности и почему на смену ему должен прийти OpenID Connect (OIDC).

Коварство SAML в том, что он в основном прост для понимания, но стоит на фундаменте из песка, костной пыли и пепла. Он работает… если считать, что проверке XML-подписей можно доверять. Но проверка XML-подписей проклята, и большинство реализаций SAML — обёртки над libxmlsec, жуткой кодовой базой на C, которую никто не читает.
— Томас Птачек (Thomas Ptacek), 2023
Как появился SAML
SAML (Security Assertion Markup Language) создал в 2002 году технический комитет SSTC организации OASIS. Уже здесь два тревожных сигнала. Во-первых, XML: у него есть достоинства, но по сравнению с JSON он весьма сложен. Во-вторых, комитет из подкомитетов — верный признак протокола по принципу «всё и сразу» (kitchen-sink). Так и вышло: в SAML втиснули сразу четыре XML-протокола безопасности:
Security Services Markup Language (S2ML) от Netegrity;
AuthXML от Securant;
XML Trust Assertion Service Specification (X-TASS) от VeriSign;
Information Technology Markup Language (ITML) от Jamcracker.
При этом потребность в таком протоколе была бесспорной. С переходом к Web 2.0 пользователям понадобился простой способ входить во множество новых веб-сервисов. Двигателем стали университеты: CAS (Йель, 2002), Shibboleth IdP (консорциум Internet2, 2003), а также ADFS от Microsoft (2003) и simpleSAMLphp от норвежской Uninett (около 2007). Все они со временем стали поддерживать SAML. Академическая среда обкатала протокол, а коммерческая индустрия превратила его в рынок на миллиарды долларов: Ping Identity (2002), OneLogin (2009), Okta (2009), Duo Security (2010).
Почти все эти компании строились на SAML. Duo выпустила свой первый SSO-продукт в 2015 году, и тут в историю вступаю я: я работал над Duo Access Gateway (DAG) — локальным (on-premises) решением на базе simpleSAMLphp. Там я годами переваривал спецификации SAML и был рядом, когда Келби Людвиг (Kelby Ludwig) нашёл обход через XML-комментарии.
Трещина в броне: атаки XSW
Ахиллесова пята SAML — атаки с обёртыванием XML-подписи (XML signature wrapping, XSW). Их исследовали с 2005 года, но ключевой я считаю работу «On Breaking SAML: Be Whoever You Want to Be» (2012): авторы проверили теорию на практике и предложили автоматический способ поиска XSW. При создании DAG она была нашей путеводной звездой. Из-за неё мы и выбрали simpleSAMLphp: PHP тогда безопасностью не славился, но simpleSAMLphp был устойчив к XSW, когда почти никто не знал, что это такое.

Прошло больше десяти лет, а XSW по-прежнему актуальны. Почему известный класс ошибок не удаётся искоренить? Чтобы ответить, нужно взглянуть на фундамент SAML — XML.
По части (не)безопасности у XML богатая история: XXE, раскрытие сущностей («billion laughs»), загрузка DTD (SSRF), инъекции в XPath/XQuery/XInclude/XSLT/CDATA. Библиотека для SAML должна закрыть всё это ещё до того, как дойдёт до собственно SAML. Плюс сама сложность формата: в XML есть теги, элементы, атрибуты, комментарии, пространства имён, схемы, CDATA, DOCTYPE и многое другое, а в JSON — ключи, значения, объекты и списки. Сложность — враг безопасности.
Пять фатальных изъянов SAML
Эти изъяны, на мой взгляд, ставят крест на будущем SAML. Их полезно учитывать и при проектировании новых протоколов: либо обходить, либо закладывать противоположные свойства.
1. Построен на XML
Комитет тут не виноват: XML был под рукой, им все пользовались. JSON «открыли» в 2001 году, как раз когда заседал комитет, и вряд ли кто-то стал бы строить протокол аутентификации на экспериментальном формате, заточенном под JavaScript, — особенно если сам пишешь в основном на Java. Количественно сравнить сложность XML и JSON (или SAML и JWT/OIDC) — тема для отдельной статьи, но то, что XML значительно сложнее, очевидно.
2. Канонизация
Канонизация (canonicalization, C14N) нужна, чтобы из хаотичного XML получать стабильный хеш. Если SP и IdP не договорятся о единообразном представлении данных, байты не совпадут, подпись не сойдётся, и аутентификация провалится. Сделать это правильно очень непросто.
Именно ошибки канонизации позволили Келби в 2018 году обойти проверку через XML-комментарии.

Они же открывают дорогу багам расхождения парсеров (parser differential) и ошибкам «туда-обратно» (round-trip), на которых строится большинство современных атак на SAML:
Coordinated disclosure of XML round-trip vulnerabilities in Go’s standard library (2020)
Abusing libxml2 quirks to bypass SAML authentication on GitHub Enterprise (2025)
Sign in as anyone: Bypassing SAML SSO authentication with parser differentials (2025)
The Fragile Lock: Novel Bypasses For SAML Authentication (2025)
3. Вложенные подписи
Близкий родственник предыдущей проблемы. Если вставлять подпись внутрь тех самых данных, которые подписываешь, ничего хорошего не выйдет.

В JWT подпись отделена от JSON-данных точкой («.»). В SAML элемент Signature вложен (enveloped) внутрь элемента Assertion. Получить побайтно совпадающее, канонически эквивалентное представление данных, одновременно их изменяя, очень трудно, а со сложными правилами канонизации XML — тем более.
4. Всё и сразу
Здесь уместен принцип YAGNI (you aren’t gonna need it — «вам это не понадобится»). Набор реально используемых частей SAML за 20 лет менялся (прощайте, SOAP и artifact binding), но сегодня 99% реализаций используют почти одинаковую структуру данных и одно и то же подмножество спецификации. Типичная SAML-аутентификация не задействует 90% стандарта, а сложность ради неиспользуемых функций никуда не девается.
Если бы я добавлял поддержку SAML во что-то новое, то помимо стандартных проверок подумал бы о том, чтобы отклонять любое сообщение, по структуре не похожее на то, что генерируют Okta, OneLogin, Google или Shib.
— Томас Птачек, 2021
5. Окостенение
SAML проектировался под другую эпоху и так и не получил нужных обновлений. Проблемы здесь скорее практические:
Независимость от транспорта. OIDC рассчитан на HTTP, а SAML нет. HTTP-привязки (bindings) для SAML есть и используются чаще всего, но они не обязательны. Гибкость нужно описать, реализовать и отладить. OIDC же перекладывает шифрование и доверенный канал на HTTPS.
Отсутствие прямой связи между сторонами. Основной поток OIDC (authorization code) предполагает, что провайдер OpenID (OP) и проверяющая сторона (RP) — аналоги IdP и SP — общаются напрямую. Тогда ответ на аутентификацию не обязан содержать всё необходимое для решения об аутентификации и авторизации: остальное можно передать по отдельному серверному каналу (backchannel). Это уменьшает размер и сложность ответа. В OIDC есть неявный поток (implicit flow) с form post, а в SAML — artifact binding для прямой связи, но оба сегодня встречаются редко.
-
Проектирование заранее. OIDC — это десятки спецификаций и RFC, которые появлялись постепенно под конкретные задачи, как в agile. Хронология развития OIDC (неполная):
OpenID Connect 1.0 (2014);
стек JOSE для JW{S,E,K,A,T}, RFC 7515–7519 (2015);
PKCE, RFC 7636 (2015);
PKCE для мобильных и нативных приложений, RFC 8252 (2017);
Device authorization grant для IoT, RFC 8628 (2019);
DPoP (Demonstrating Proof of Possession) для сценариев с MFA, RFC 9449 (2023);
PKCE для SPA, RFC 10017 (2026).
Интересен и исторический контекст. SAML взрослел в эпоху VPN и сегментированных сетей: если IdP или SP стоял за корпоративным файрволом и не мог связаться с другой стороной, внедрение срывалось, а вендор терял сделку. В 2014 году BeyondCorp от Google и концепция нулевого доверия (zero trust) перевернули этот подход. К тому же SAML не предвидел революцию мобильных устройств, SPA и IoT, и ответа на них у него не нашлось. Всё это положило начало медленному упадку протокола. Гибкость и слабая связанность позволяют быстро адаптироваться к меняющейся ИТ-среде.
Все дороги ведут в OIDC
Идеальных протоколов не бывает, но решение очевидно. Насколько я могу судить, единственный сценарий, где SAML выигрывал, — сети, в которых SP и IdP не могут связаться напрямую. Неявный поток OIDC с form post закрывает и его. Это один из самых чистых планов миграции, какие я встречал.
Если вы поставщик услуг (SP) — просто поддерживайте OIDC и откажитесь от SAML. Fly.io и Tailscale, судя по всему, уже так делают:
Пока нам удаётся держаться только OIDC. Tailscale тоже. Если это получается у Tailscale — с учётом того, кому они продают, — думаю, получится и у большинства компаний. Постарайтесь обойтись без SAML. Как вендор вы часто конкурируете с компаниями, у которых вообще нет полноценной интеграции с SSO.
— Томас Птачек, 2024
Если вы провайдер идентификации (IdP) — путь может оказаться долгим, в зависимости от числа клиентов. Но дорога давно протоптана:
Составьте план отказа от SAML и сообщите о нём клиентам.
Перестаньте подключать новых клиентов через SAML.
Предложите существующим клиентам эквивалентные конфигурации OIDC.
Назначьте дату отключения — и за работу.
Итоги
SAML достойно прослужил 25 лет: породил индустрию SSO, защитил бессчётное число аутентификаций, упростил вход в десятки сервисов и принёс миллиарды долларов экономического эффекта. Его создателям стоит сказать спасибо. Но сегодня SAML — прежде всего отличный учебный пример того, как проектируются и развиваются протоколы.
Комментарии (3)

koil
25.09.2026 11:13Статья притянута, какое отношение имеет сам протокол к ошибкам в реализации не понятно. Heartbleed в OpenSSL точно так же не имеет никакого отношения к протоколу, хотя и затронул всех глобально.

Heggi
25.09.2026 11:13Если правильная реализация недостижима или крайне затруднена, то может ну его нафиг этот протокол? К тому же есть альтернативы.
mayorovp
Столько всего по-написано, но нет никаких подробностей. Если основная атака на SAML - и правда XSW, то тут всё просто, и виноват не столько XML, сколько стандарт XMLDSig.
XMLDSig - это очень универсальный механизм, который позволяет выборочно подписать любой набор узлов в произвольном XML документе. Библиотеки, проверяющие XMLDSig, тоже очень универсальны и внимательно следят что именно было подписано. При этом они не проверяют что именно должно было быть подписано. Эта проверка в идеале должна делаться снаружи, но тут вылезает ещё одна беда XMLDSig - это очень сложный стандарт, который мало кто понимает. Соблазн оставить всё на библиотеку со словами “ну, не может же всё быть настолько плохо, наверняка там всё продумано” слишком велик.
Как результат, если удаётся найти в XML-документе какое-нибудь место, куда можно вставить другой такой документ целиком - то можно взять подпись от внутреннего документа и положить её на место подписи внешнего, и много программ посчитают такую подпись валидной.