diff --git a/changelog.md b/changelog.md index c8acb1c4..39d9b646 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ # RubySaml Changelog +### 1.12.3 (Sep 10, 2024) +* Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector + ### 1.12.2 (Apr 08, 2022) * [575](https://github.com/onelogin/ruby-saml/pull/575) Fix SloLogoutresponse bug on LogoutRequest diff --git a/lib/onelogin/ruby-saml/version.rb b/lib/onelogin/ruby-saml/version.rb index 3416b240..2c4f603a 100644 --- a/lib/onelogin/ruby-saml/version.rb +++ b/lib/onelogin/ruby-saml/version.rb @@ -1,5 +1,5 @@ module OneLogin module RubySaml - VERSION = '1.12.2' + VERSION = '1.12.3' end end diff --git a/lib/xml_security.rb b/lib/xml_security.rb index 86b89ac3..652b8ae6 100644 --- a/lib/xml_security.rb +++ b/lib/xml_security.rb @@ -312,17 +312,30 @@ def validate_signature(base64_cert, soft = true) canon_string = noko_signed_info_element.canonicalize(canon_algorithm) noko_sig_element.remove + # get signed info + signed_info_element = REXML::XPath.first( + sig_element, + "./ds:SignedInfo", + { "ds" => DSIG } + ) + # get inclusive namespaces inclusive_namespaces = extract_inclusive_namespaces # check digests - ref = REXML::XPath.first(sig_element, "//ds:Reference", {"ds"=>DSIG}) + ref = REXML::XPath.first(signed_info_element, "./ds:Reference", {"ds"=>DSIG}) - hashed_element = document.at_xpath("//*[@ID=$id]", nil, { 'id' => extract_signed_element_id }) + reference_nodes = document.xpath("//*[@ID=$id]", nil, { 'id' => extract_signed_element_id }) + + if reference_nodes.length > 1 # ensures no elements with same ID to prevent signature wrapping attack. + return append_error("Duplicated IDs found", soft) + end + + hashed_element = reference_nodes[0] canon_algorithm = canon_algorithm REXML::XPath.first( - ref, - '//ds:CanonicalizationMethod', + signed_info_element, + './ds:CanonicalizationMethod', { "ds" => DSIG } ) @@ -332,13 +345,13 @@ def validate_signature(base64_cert, soft = true) digest_algorithm = algorithm(REXML::XPath.first( ref, - "//ds:DigestMethod", + "./ds:DigestMethod", { "ds" => DSIG } )) hash = digest_algorithm.digest(canon_hashed_element) encoded_digest_value = REXML::XPath.first( ref, - "//ds:DigestValue", + "./ds:DigestValue", { "ds" => DSIG } ) digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value)) @@ -364,7 +377,7 @@ def validate_signature(base64_cert, soft = true) def process_transforms(ref, canon_algorithm) transforms = REXML::XPath.match( ref, - "//ds:Transforms/ds:Transform", + "./ds:Transforms/ds:Transform", { "ds" => DSIG } )