From a7b1f2ede2137333f61ef0d509274ecb7cf4c31e Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 10 Sep 2024 21:26:13 +0200 Subject: [PATCH] Use correct XPaths and resolve to correct elements. Block references that resolve to multiple nodes to prevent signature wrapping attacks --- lib/ruby_saml/xml/signed_document.rb | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/ruby_saml/xml/signed_document.rb b/lib/ruby_saml/xml/signed_document.rb index 444a062a..03a47608 100644 --- a/lib/ruby_saml/xml/signed_document.rb +++ b/lib/ruby_saml/xml/signed_document.rb @@ -129,17 +129,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("Digest mismatch. Duplicated ID 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 } ) @@ -155,7 +168,7 @@ def validate_signature(base64_cert, soft = true) hash = digest_algorithm.digest(canon_hashed_element) encoded_digest_value = REXML::XPath.first( ref, - '//ds:DigestValue', + './ds:DigestValue', { 'ds' => DSIG } ) digest_value = Base64.decode64(RubySaml::Utils.element_text(encoded_digest_value)) @@ -181,7 +194,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 } )