ruby-saml 1.12.3 → 1.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +65 -0
- data/changelog.md +3 -0
- data/lib/onelogin/ruby-saml/logoutresponse.rb +2 -1
- data/lib/onelogin/ruby-saml/response.rb +10 -3
- data/lib/onelogin/ruby-saml/saml_message.rb +17 -6
- data/lib/onelogin/ruby-saml/settings.rb +2 -0
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +2 -1
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +45 -13
- data/ruby-saml.gemspec +9 -1
- metadata +32 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c7575733bfd28deef0060550daede564f894f423c6749d71007b475694e5409
|
4
|
+
data.tar.gz: 4f925319b91da371cecdcb0946f617b056b9f6fbe1665dd5e5ccf2afff9f24a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b378e2c5e13810c280783154c7fe25a0c287e89a12698aa65a9d33873b44987b5f2cd0f0676b797fdefad86f993975b10d29a9a5665513ff68ed064efd7c41c1
|
7
|
+
data.tar.gz: 1b370d8753b3104ae3b8bc9e1f1c2ddd067abee96a5ea9b896cf2e0571e6999564a92df78aa5d55266821185a77770cf2f2166a2b944fde5ed6216e2b3c4b7f1
|
@@ -0,0 +1,65 @@
|
|
1
|
+
name: ruby-saml CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
name: Unit test
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
os:
|
12
|
+
- ubuntu-20.04
|
13
|
+
- macos-latest
|
14
|
+
- windows-latest
|
15
|
+
ruby-version:
|
16
|
+
- 2.1
|
17
|
+
- 2.2
|
18
|
+
- 2.3
|
19
|
+
- 2.4
|
20
|
+
- 2.5
|
21
|
+
- 2.6
|
22
|
+
- 2.7
|
23
|
+
- 3.0
|
24
|
+
- jruby-9.1
|
25
|
+
- jruby-9.2
|
26
|
+
exclude:
|
27
|
+
- os: macos-latest
|
28
|
+
ruby-version: 2.1
|
29
|
+
- os: macos-latest
|
30
|
+
ruby-version: 2.2
|
31
|
+
- os: macos-latest
|
32
|
+
ruby-version: 2.3
|
33
|
+
- os: macos-latest
|
34
|
+
ruby-version: 2.4
|
35
|
+
- os: macos-latest
|
36
|
+
ruby-version: 2.5
|
37
|
+
- os: macos-latest
|
38
|
+
ruby-version: jruby-9.1
|
39
|
+
- os: macos-latest
|
40
|
+
ruby-version: jruby-9.2
|
41
|
+
- os: windows-latest
|
42
|
+
ruby-version: 2.1
|
43
|
+
- os: windows-latest
|
44
|
+
ruby-version: jruby-9.1
|
45
|
+
- os: windows-latest
|
46
|
+
ruby-version: jruby-9.2
|
47
|
+
- os: windows-latest
|
48
|
+
ruby-version: jruby-9.3
|
49
|
+
- os: windows-latest
|
50
|
+
ruby-version: jruby-9.4
|
51
|
+
- os: windows-latest
|
52
|
+
ruby-version: truffleruby
|
53
|
+
runs-on: ${{ matrix.os }}
|
54
|
+
steps:
|
55
|
+
- uses: actions/checkout@v4
|
56
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
57
|
+
uses: ruby/setup-ruby@v1
|
58
|
+
with:
|
59
|
+
ruby-version: ${{ matrix.ruby-version }}
|
60
|
+
|
61
|
+
- name: Install dependencies
|
62
|
+
run: bundle install
|
63
|
+
|
64
|
+
- name: Run tests
|
65
|
+
run: bundle exec rake
|
data/changelog.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# RubySaml Changelog
|
2
|
+
### 1.12.4 (Mar 12, 2025)
|
3
|
+
* Fix vulnerabilities: CVE-2025-25291, CVE-2025-25292: SAML authentication bypass via Signature Wrapping attack allowed due parser differential.
|
4
|
+
* Fix vulnerability: CVE-2025-25293: Potential DOS abusing of compressed messages.
|
2
5
|
|
3
6
|
### 1.12.3 (Sep 10, 2024)
|
4
7
|
* Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector
|
@@ -150,7 +150,8 @@ module OneLogin
|
|
150
150
|
# @raise [ValidationError] if soft == false and validation fails
|
151
151
|
#
|
152
152
|
def validate_structure
|
153
|
-
|
153
|
+
check_malformed_doc = check_malformed_doc?(settings)
|
154
|
+
unless valid_saml?(document, soft, check_malformed_doc)
|
154
155
|
return append_error("Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd")
|
155
156
|
end
|
156
157
|
|
@@ -425,12 +425,13 @@ module OneLogin
|
|
425
425
|
#
|
426
426
|
def validate_structure
|
427
427
|
structure_error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
428
|
-
|
428
|
+
check_malformed_doc = check_malformed_doc_enabled?
|
429
|
+
unless valid_saml?(document, soft, check_malformed_doc)
|
429
430
|
return append_error(structure_error_msg)
|
430
431
|
end
|
431
432
|
|
432
433
|
unless decrypted_document.nil?
|
433
|
-
unless valid_saml?(decrypted_document, soft)
|
434
|
+
unless valid_saml?(decrypted_document, soft, check_malformed_doc)
|
434
435
|
return append_error(structure_error_msg)
|
435
436
|
end
|
436
437
|
end
|
@@ -865,6 +866,8 @@ module OneLogin
|
|
865
866
|
fingerprint = settings.get_fingerprint
|
866
867
|
opts[:cert] = idp_cert
|
867
868
|
|
869
|
+
check_malformed_doc = check_malformed_doc_enabled?
|
870
|
+
opts[:check_malformed_doc] = check_malformed_doc
|
868
871
|
if fingerprint && doc.validate_document(fingerprint, @soft, opts)
|
869
872
|
if settings.security[:check_idp_cert_expiration]
|
870
873
|
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
@@ -879,7 +882,7 @@ module OneLogin
|
|
879
882
|
valid = false
|
880
883
|
expired = false
|
881
884
|
idp_certs[:signing].each do |idp_cert|
|
882
|
-
valid = doc.validate_document_with_cert(idp_cert, true)
|
885
|
+
valid = doc.validate_document_with_cert(idp_cert, true, check_malformed_doc)
|
883
886
|
if valid
|
884
887
|
if settings.security[:check_idp_cert_expiration]
|
885
888
|
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
@@ -1063,6 +1066,10 @@ module OneLogin
|
|
1063
1066
|
Time.parse(node.attributes[attribute])
|
1064
1067
|
end
|
1065
1068
|
end
|
1069
|
+
|
1070
|
+
def check_malformed_doc_enabled?
|
1071
|
+
check_malformed_doc?(settings)
|
1072
|
+
end
|
1066
1073
|
end
|
1067
1074
|
end
|
1068
1075
|
end
|
@@ -63,14 +63,13 @@ module OneLogin
|
|
63
63
|
# Validates the SAML Message against the specified schema.
|
64
64
|
# @param document [REXML::Document] The message that will be validated
|
65
65
|
# @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the message is invalid or not)
|
66
|
+
# @param check_malformed_doc [Boolean] check_malformed_doc Enable or Disable the check for malformed XML
|
66
67
|
# @return [Boolean] True if the XML is valid, otherwise False, if soft=True
|
67
68
|
# @raise [ValidationError] if soft == false and validation fails
|
68
69
|
#
|
69
|
-
def valid_saml?(document, soft = true)
|
70
|
+
def valid_saml?(document, soft = true, check_malformed_doc = true)
|
70
71
|
begin
|
71
|
-
xml =
|
72
|
-
config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
|
73
|
-
end
|
72
|
+
xml = XMLSecurity::BaseDocument.safe_load_xml(document, check_malformed_doc)
|
74
73
|
rescue Exception => error
|
75
74
|
return false if soft
|
76
75
|
raise ValidationError.new("XML load failed: #{error.message}")
|
@@ -97,10 +96,16 @@ module OneLogin
|
|
97
96
|
|
98
97
|
decoded = decode(saml)
|
99
98
|
begin
|
100
|
-
|
99
|
+
message = inflate(decoded)
|
101
100
|
rescue
|
102
|
-
|
101
|
+
message = decoded
|
102
|
+
end
|
103
|
+
|
104
|
+
if message.bytesize > MAX_BYTE_SIZE
|
105
|
+
raise ValidationError.new("Encoded SAML Message exceeds " + MAX_BYTE_SIZE.to_s + " bytes, so was rejected")
|
103
106
|
end
|
107
|
+
|
108
|
+
message
|
104
109
|
end
|
105
110
|
|
106
111
|
# Deflate, base64 encode and url-encode a SAML Message (To be used in the HTTP-redirect binding)
|
@@ -157,6 +162,12 @@ module OneLogin
|
|
157
162
|
def deflate(inflated)
|
158
163
|
Zlib::Deflate.deflate(inflated, 9)[2..-5]
|
159
164
|
end
|
165
|
+
|
166
|
+
def check_malformed_doc?(settings)
|
167
|
+
default_value = OneLogin::RubySaml::Settings::DEFAULTS[:check_malformed_doc]
|
168
|
+
|
169
|
+
settings.nil? ? default_value : settings.check_malformed_doc
|
170
|
+
end
|
160
171
|
end
|
161
172
|
end
|
162
173
|
end
|
@@ -53,6 +53,7 @@ module OneLogin
|
|
53
53
|
attr_accessor :compress_request
|
54
54
|
attr_accessor :compress_response
|
55
55
|
attr_accessor :double_quote_xml_attribute_values
|
56
|
+
attr_accessor :check_malformed_doc
|
56
57
|
attr_accessor :passive
|
57
58
|
attr_accessor :protocol_binding
|
58
59
|
attr_accessor :attributes_index
|
@@ -259,6 +260,7 @@ module OneLogin
|
|
259
260
|
:compress_request => true,
|
260
261
|
:compress_response => true,
|
261
262
|
:soft => true,
|
263
|
+
:check_malformed_doc => true,
|
262
264
|
:double_quote_xml_attribute_values => false,
|
263
265
|
:security => {
|
264
266
|
:authn_requests_signed => false,
|
@@ -199,7 +199,8 @@ module OneLogin
|
|
199
199
|
# @raise [ValidationError] if soft == false and validation fails
|
200
200
|
#
|
201
201
|
def validate_structure
|
202
|
-
|
202
|
+
check_malformed_doc = check_malformed_doc?(settings)
|
203
|
+
unless valid_saml?(document, soft, check_malformed_doc)
|
203
204
|
return append_error("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd")
|
204
205
|
end
|
205
206
|
|
data/lib/xml_security.rb
CHANGED
@@ -42,6 +42,36 @@ module XMLSecurity
|
|
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 @@ module XMLSecurity
|
|
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 =
|
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 @@ module XMLSecurity
|
|
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 =
|
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 @@ module XMLSecurity
|
|
237
263
|
end
|
238
264
|
end
|
239
265
|
end
|
240
|
-
|
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 @@ module XMLSecurity
|
|
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
|
-
|
273
|
-
|
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
|
data/ruby-saml.gemspec
CHANGED
@@ -47,12 +47,20 @@ Gem::Specification.new do |s|
|
|
47
47
|
s.add_runtime_dependency('rexml')
|
48
48
|
end
|
49
49
|
|
50
|
+
s.add_development_dependency('simplecov', '<0.22.0')
|
51
|
+
if RUBY_VERSION < '2.4.1'
|
52
|
+
s.add_development_dependency('simplecov-lcov', '<0.8.0')
|
53
|
+
s.add_development_dependency('term-ansicolor', '1.2.2')
|
54
|
+
s.add_development_dependency('mime-types', '<3.6.0')
|
55
|
+
else
|
56
|
+
s.add_development_dependency('simplecov-lcov', '>0.7.0')
|
57
|
+
end
|
58
|
+
|
50
59
|
s.add_development_dependency('coveralls')
|
51
60
|
s.add_development_dependency('minitest', '~> 5.5')
|
52
61
|
s.add_development_dependency('mocha', '~> 0.14')
|
53
62
|
s.add_development_dependency('rake', '~> 10')
|
54
63
|
s.add_development_dependency('shoulda', '~> 2.11')
|
55
|
-
s.add_development_dependency('simplecov')
|
56
64
|
s.add_development_dependency('systemu', '~> 2')
|
57
65
|
s.add_development_dependency('timecop', '<= 0.6.0')
|
58
66
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-saml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.12.
|
4
|
+
version: 1.12.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OneLogin LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "<"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.22.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "<"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.22.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov-lcov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.7.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.7.0
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: coveralls
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,20 +136,6 @@ dependencies:
|
|
108
136
|
- - "~>"
|
109
137
|
- !ruby/object:Gem::Version
|
110
138
|
version: '2.11'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: simplecov
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: systemu
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -173,6 +187,7 @@ extra_rdoc_files:
|
|
173
187
|
- README.md
|
174
188
|
files:
|
175
189
|
- ".document"
|
190
|
+
- ".github/workflows/test.yml"
|
176
191
|
- ".gitignore"
|
177
192
|
- ".travis.yml"
|
178
193
|
- Gemfile
|
@@ -236,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
236
251
|
- !ruby/object:Gem::Version
|
237
252
|
version: '0'
|
238
253
|
requirements: []
|
239
|
-
rubygems_version: 3.
|
254
|
+
rubygems_version: 3.5.18
|
240
255
|
signing_key:
|
241
256
|
specification_version: 4
|
242
257
|
summary: SAML Ruby Tookit
|