@@ -42,6 +42,36 @@ class BaseDocument < REXML::Document
42
42
NOKOGIRI_OPTIONS = Nokogiri ::XML ::ParseOptions ::STRICT |
43
43
Nokogiri ::XML ::ParseOptions ::NONET
44
44
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
+
45
75
def canon_algorithm ( element )
46
76
algorithm = element
47
77
if algorithm . is_a? ( REXML ::Element )
@@ -114,10 +144,8 @@ def uuid
114
144
#<KeyInfo />
115
145
#<Object />
116
146
#</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 )
121
149
122
150
signature_element = REXML ::Element . new ( "ds:Signature" ) . add_namespace ( 'ds' , DSIG )
123
151
signed_info_element = signature_element . add_element ( "ds:SignedInfo" )
@@ -139,9 +167,7 @@ def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_
139
167
reference_element . add_element ( "ds:DigestValue" ) . text = compute_digest ( canon_doc , algorithm ( digest_method_element ) )
140
168
141
169
# 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 )
145
171
146
172
noko_signed_info_element = noko_sig_element . at_xpath ( '//ds:Signature/ds:SignedInfo' , 'ds' => DSIG )
147
173
canon_string = noko_signed_info_element . canonicalize ( canon_algorithm ( C14N ) )
@@ -237,10 +263,12 @@ def validate_document(idp_cert_fingerprint, soft = true, options = {})
237
263
end
238
264
end
239
265
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 )
241
269
end
242
270
243
- def validate_document_with_cert ( idp_cert , soft = true )
271
+ def validate_document_with_cert ( idp_cert , soft = true , check_malformed_doc = true )
244
272
# get cert from response
245
273
cert_element = REXML ::XPath . first (
246
274
self ,
@@ -264,13 +292,17 @@ def validate_document_with_cert(idp_cert, soft = true)
264
292
else
265
293
base64_cert = Base64 . encode64 ( idp_cert . to_pem )
266
294
end
267
- validate_signature ( base64_cert , true )
295
+ validate_signature ( base64_cert , true , check_malformed_doc )
268
296
end
269
297
270
- def validate_signature ( base64_cert , soft = true )
298
+ def validate_signature ( base64_cert , soft = true , check_malformed_doc = true )
271
299
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 } " )
274
306
end
275
307
276
308
# create a rexml document
0 commit comments