@@ -42,6 +42,36 @@ class BaseDocument < REXML::Document
4242 NOKOGIRI_OPTIONS = Nokogiri ::XML ::ParseOptions ::STRICT |
4343 Nokogiri ::XML ::ParseOptions ::NONET
4444
45+ # Safety load the SAML Message XML
46+ # @param document [REXML::Document] The message to be loaded
47+ # @param check_malformed_doc [Boolean] check_malformed_doc Enable or Disable the check for malformed XML
48+ # @return [Nokogiri::XML] The nokogiri document
49+ # @raise [ValidationError] If there was a problem loading the SAML Message XML
50+ def self . safe_load_xml ( document , check_malformed_doc = true )
51+ doc_str = document . to_s
52+ if doc_str . include? ( "<!DOCTYPE" )
53+ raise StandardError . new ( "Dangerous XML detected. No Doctype nodes allowed" )
54+ end
55+
56+ begin
57+ xml = Nokogiri ::XML ( doc_str ) do |config |
58+ config . options = self ::NOKOGIRI_OPTIONS
59+ end
60+ rescue StandardError => error
61+ raise StandardError . new ( error . message )
62+ end
63+
64+ if xml . internal_subset
65+ raise StandardError . new ( "Dangerous XML detected. No Doctype nodes allowed" )
66+ end
67+
68+ unless xml . errors . empty?
69+ raise StandardError . new ( "There were XML errors when parsing: #{ xml . errors } " ) if check_malformed_doc
70+ end
71+
72+ xml
73+ end
74+
4575 def canon_algorithm ( element )
4676 algorithm = element
4777 if algorithm . is_a? ( REXML ::Element )
@@ -114,10 +144,8 @@ def uuid
114144 #<KeyInfo />
115145 #<Object />
116146 #</Signature>
117- def sign_document ( private_key , certificate , signature_method = RSA_SHA1 , digest_method = SHA1 )
118- noko = Nokogiri ::XML ( self . to_s ) do |config |
119- config . options = XMLSecurity ::BaseDocument ::NOKOGIRI_OPTIONS
120- end
147+ def sign_document ( private_key , certificate , signature_method = RSA_SHA1 , digest_method = SHA1 , check_malformed_doc = true )
148+ noko = XMLSecurity ::BaseDocument . safe_load_xml ( self . to_s , check_malformed_doc )
121149
122150 signature_element = REXML ::Element . new ( "ds:Signature" ) . add_namespace ( 'ds' , DSIG )
123151 signed_info_element = signature_element . add_element ( "ds:SignedInfo" )
@@ -139,9 +167,7 @@ def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_
139167 reference_element . add_element ( "ds:DigestValue" ) . text = compute_digest ( canon_doc , algorithm ( digest_method_element ) )
140168
141169 # add SignatureValue
142- noko_sig_element = Nokogiri ::XML ( signature_element . to_s ) do |config |
143- config . options = XMLSecurity ::BaseDocument ::NOKOGIRI_OPTIONS
144- end
170+ noko_sig_element = XMLSecurity ::BaseDocument . safe_load_xml ( signature_element . to_s , check_malformed_doc )
145171
146172 noko_signed_info_element = noko_sig_element . at_xpath ( '//ds:Signature/ds:SignedInfo' , 'ds' => DSIG )
147173 canon_string = noko_signed_info_element . canonicalize ( canon_algorithm ( C14N ) )
@@ -237,10 +263,12 @@ def validate_document(idp_cert_fingerprint, soft = true, options = {})
237263 end
238264 end
239265 end
240- validate_signature ( base64_cert , soft )
266+ check_malformed_doc = true
267+ check_malformed_doc = options [ :check_malformed_doc ] if options . key? ( :check_malformed_doc )
268+ validate_signature ( base64_cert , soft , check_malformed_doc )
241269 end
242270
243- def validate_document_with_cert ( idp_cert , soft = true )
271+ def validate_document_with_cert ( idp_cert , soft = true , check_malformed_doc = true )
244272 # get cert from response
245273 cert_element = REXML ::XPath . first (
246274 self ,
@@ -264,13 +292,17 @@ def validate_document_with_cert(idp_cert, soft = true)
264292 else
265293 base64_cert = Base64 . encode64 ( idp_cert . to_pem )
266294 end
267- validate_signature ( base64_cert , true )
295+ validate_signature ( base64_cert , true , check_malformed_doc )
268296 end
269297
270- def validate_signature ( base64_cert , soft = true )
298+ def validate_signature ( base64_cert , soft = true , check_malformed_doc = true )
271299
272- document = Nokogiri ::XML ( self . to_s ) do |config |
273- config . options = XMLSecurity ::BaseDocument ::NOKOGIRI_OPTIONS
300+ begin
301+ document = XMLSecurity ::BaseDocument . safe_load_xml ( self , check_malformed_doc )
302+ rescue StandardError => error
303+ @errors << error . message
304+ return false if soft
305+ raise ValidationError . new ( "XML load failed: #{ error . message } " )
274306 end
275307
276308 # create a rexml document
0 commit comments