[Перевод] Почему SAML ломают снова и снова: пять фатальных изъянов протокола аутентификации

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 — прежде всего отличный учебный пример того, как проектируются и развиваются протоколы.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.