Проблема
XML Signature может быть математически корректной, но итоговая authentication decision все равно окажется неверной, если разные части SAML-библиотеки по-разному понимают namespaces, duplicate IDs, transforms или расположение assertion.
Исследование PortSwigger показывает, насколько хрупкой становится архитектура при нескольких parser passes и несогласованной canonicalization. Для production-команды важен не exploit payload, а контроль инварианта: подписан и использован один и тот же объект.
Решение: parse once и consume by reference
Входной документ должен иметь жесткий size limit, запрет DTD и external entities, строгую схему и уникальные ID. После единственного parse verifier возвращает ссылку на проверенный assertion node, а business logic читает claims только из этой ссылки без повторного XPath и повторного parse.
Audience, recipient, issuer, time window, InResponseTo и allowed algorithms должны проверяться как единый policy result. Любая неоднозначность должна завершаться fail closed и оставлять decision record.
Что проверить руками
- Какая библиотека и какая версия реально обрабатывают SAML в каждом service provider.
- Есть ли второй XML parser, повторный XPath или ручное извлечение claims после verify.
- Отклоняются ли duplicate IDs, неожиданные transforms, неизвестные algorithms и лишние assertions.
- Проверяются ли audience, recipient, ACS URL, issuer, replay cache и clock skew.
- Есть ли regression corpus из malformed и неоднозначных документов без production credentials.
Безопасный пример: same-node contract
Псевдокод показывает архитектурный контракт. Конкретные API зависят от выбранной поддерживаемой SAML-библиотеки.
def authenticate_saml(raw_response, policy):
document = strict_parse_once(
raw_response,
max_bytes=256_000,
allow_dtd=False,
allow_external_entities=False,
require_unique_ids=True,
)
result = verify_response(document, policy)
if not result.valid or result.assertion_node is None:
raise AuthenticationDenied(result.reason)
# Claims are consumed from the exact node returned by verification.
claims = claims_from_verified_node(result.assertion_node)
return policy.authorize(claims, result.context)
Типичные ошибки
- Считать успешную crypto verification полной проверкой SAML response.
- Патчить один parser edge case без regression corpus и архитектурного инварианта.
- Разрешать слишком широкий набор algorithms и transforms ради совместимости.
- Логировать полный assertion с PII и session-sensitive claims.
Evidence и критерий ретеста
- Inventory содержит SP, IdP, library version, owner и supported algorithm policy.
- Unit tests доказывают, что verify и claim consumption используют один node identity.
- Regression corpus стабильно отклоняется после обновления library и parser dependencies.
Материал написан Virusologia с нуля как авторская инженерная интерпретация. Факты сверены по первичным публикациям, формулировки и практические выводы не копируют исходные тексты.
Материал предназначен для защиты, controlled labs и аудита систем, которыми вы владеете или на проверку которых имеете явное разрешение. Он не содержит эксплуатационных payload-цепочек или инструкций для несанкционированного доступа.