Skip to content

SYSTEM Cited by 1 source

ruby-saml

ruby-saml (github.com/SAML-Toolkits/ruby-saml) is an open-source Ruby library implementing the service-provider (SP) side of SAML SSO — response parsing, signature verification, assertion validation. It is the dominant SAML-SP implementation in the Ruby ecosystem and is consumed directly or via omniauth-saml by a long tail of Rails applications and notably by GitLab.

Architecture relevant to security

ruby-saml's xml_security.rb verifies a SAML response by combining two XML parsers on the same input document:

  • REXML — pure-Ruby XML parser (Ruby stdlib). Used for locating the <ds:Signature> element, extracting the <ds:SignatureValue>, locating <ds:SignedInfo>, extracting the <ds:Reference>, and extracting the <ds:DigestValue>.
  • Nokogiri — wrapper over libxml2 / libgumbo / Xerces (via JRuby). Added because REXML lacks canonicalisation support. Used to canonicalise <ds:SignedInfo> and to look up the referenced <Assertion> by ID attribute and canonicalise it for digest hashing.

The verification path has two asymmetric chains:

  1. <ds:SignatureValue> (REXML) is verified against canonicalised <ds:SignedInfo> (Nokogiri).
  2. <ds:DigestValue> (REXML) is compared to the hash of canonicalised <Assertion> looked up by ID (Nokogiri).

Neither chain pins its two endpoints to the same byte range. If REXML and Nokogiri disagree about which <ds:Signature> element an XPath query returns, the signature and the digest can each verify in isolation against pieces of different signature elements in the same document — an authentication bypass.

CVE-2025-25291 + CVE-2025-25292 (2025-03-12)

Two distinct parser-differential exploits, independently found by ahacker1 (XML-roundtrip technique, Mattermost 2021 lineage) and by the GitHub Security Lab (coverage-guided fuzzing with Trail of Bits' ruzzy). Both yielded authentication bypass against any SP using ruby-saml ≤ 1.17.0. Attacker precondition: possession of any single valid signature produced by the target organisation's IdP signing key — obtainable from any signed assertion or, in some deployments, from publicly-published signed IdP metadata.

Fixed in ruby-saml 1.18.0. The fix is gadget-specific — it re-uses the already-extracted <SignedInfo> bytes as the source for the digest comparison rather than re-querying the document — but does not remove the second parser, since doing so would break backwards-compatible API callers. Structural removal of one parser PR #736 is planned for a future major release.

Historical context

  • 1.17.0 and earlier — vulnerable to CVE-2025-25291 + CVE-2025-25292.
  • October 2024 — prior authentication bypass CVE-2024-45409 reported by ahacker1, a different multi-signature SAML attack; ruby-saml added a guard requiring exactly one element with a given ID. This guard is still in place in 1.17.0 — the 2025 parser differentials route around it by placing the second signature in a <StatusDetail> element the ID-uniqueness check doesn't reach.
  • 1.18.0 (2025-03-12) — parser-differential fix for both 2025 CVEs.
  • GitHub used ruby-saml until 2014, then moved to an in-house SAML implementation; the 2024-25 disclosure cycle began when GitHub was evaluating re-adoption of ruby-saml and opened a private bug-bounty engagement.

Downstream consumers to audit

  • omniauth-saml — the OmniAuth strategy wrapper around ruby-saml; applications depending on a fixed ruby-saml version transitively must use an omniauth-saml release that references it.
  • GitLab — confirmed exploitable instance, notified pre-disclosure; GitLab on-prem + SaaS both needed patching.
  • Long tail of Rails applications using ruby-saml directly or via omniauth-saml.

Seen in

Last updated · 319 distilled / 1,201 read