Skip to content

Commit

Permalink
Merge branch 'v2.x' into remove-compress-request-response-parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyshields authored Jul 9, 2024
2 parents 0df35e6 + a3d2045 commit 38e3678
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 112 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Create namespace alias `OneLogin = Object` for backward compatibility, to be removed in version `2.1.0`.
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Change directly structure from `lib/onelogin/ruby-saml` to `lib/ruby_saml`.
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Move schema files from `lib/onelogin/schemas` to `lib/ruby_saml/schemas`.
* [#686](https://github.com/SAML-Toolkits/ruby-saml/pull/686) Use SHA-256 as the default hashing algorithm everywhere instead of SHA-1, including signatures, fingerprints, and digests.
* [#689](https://github.com/SAML-Toolkits/ruby-saml/pull/689) Remove `settings.compress_request` and `settings.compess_response` parameters.

### 1.17.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def saml_settings
settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha256"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
Expand Down
22 changes: 20 additions & 2 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

## Updating from 1.17.x to 2.0.0

### Before Upgrading
### Before upgrading

Before attempting to upgrade to `2.0.0`:
- Upgrade your project to minimum Ruby 3.0, JRuby 9.4, or TruffleRuby 22.
- Upgrade RubySaml to `1.17.x`. Note that RubySaml `1.17.x` is compatible with up to Ruby 3.3.

### Root Namespace Changed to RubySaml
### Root namespace changed to RubySaml

RubySaml version `2.0.0` changes the root namespace from `OneLogin::RubySaml::` to just `RubySaml::`. This will require you
to search your codebase for the string `OneLogin::` and remove it as appropriate. Aside from this namespace change,
Expand All @@ -17,6 +17,24 @@ the class names themselves have intentionally been kept the same.
For backward compatibility, the alias `OneLogin = Object` has been set, so `OneLogin::RubySaml::` will still work.
This alias will be removed in RubySaml version `2.1.0`.

### Security: Change default hashing algorithm to SHA-256 (was SHA-1)

For security reasons, RubySaml version `2.0.0` uses SHA-256 as its default hashing algorithm everywhere
instead of the now-obsolete SHA-1. This affects:
- The default signature and digest algorithms used when generating SP metadata.
- The default signature algorithm used when generating SP messages such as AuthnRequests.
- The default fingerprint of IdP metadata (`:idp_cert_fingerprint` as generated by `RubySaml::IdpMetadataParser`)

To preserve the old insecure SHA-1 behavior *(not recommended)*, you may set `RubySaml::Settings` as follows:

```ruby
# Preserve RubySaml 1.x insecure SHA-1 behavior
settings = RubySaml::Settings.new
settings.idp_cert_fingerprint_algorithm = XMLSecurity::Document::SHA1
settings.security[:digest_method] = XMLSecurity::Document::SHA1
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
```

### Removal of Compression Settings

The `settings.compress_request` and `settings.compress_response` parameters have been removed.
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby_saml/idp_metadata_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ def certificates

# @return [String|nil] the fingerpint of the X509Certificate if it exists
#
def fingerprint(certificate, fingerprint_algorithm = XMLSecurity::Document::SHA1)
def fingerprint(certificate, fingerprint_algorithm = XMLSecurity::Document::SHA256)
@fingerprint ||= begin
return unless certificate

Expand Down
6 changes: 3 additions & 3 deletions lib/ruby_saml/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def get_binding(value)
DEFAULTS = {
assertion_consumer_service_binding: Utils::BINDINGS[:post],
single_logout_service_binding: Utils::BINDINGS[:redirect],
idp_cert_fingerprint_algorithm: XMLSecurity::Document::SHA1,
idp_cert_fingerprint_algorithm: XMLSecurity::Document::SHA256,
message_max_bytesize: 250_000,
soft: true,
double_quote_xml_attribute_values: false,
Expand All @@ -288,8 +288,8 @@ def get_binding(value)
want_name_id: false,
metadata_signed: false,
embed_sign: false, # Deprecated
digest_method: XMLSecurity::Document::SHA1,
signature_method: XMLSecurity::Document::RSA_SHA1,
digest_method: XMLSecurity::Document::SHA256,
signature_method: XMLSecurity::Document::RSA_SHA256,
check_idp_cert_expiration: false,
check_sp_cert_expiration: false,
strict_audience_validation: false,
Expand Down
8 changes: 4 additions & 4 deletions lib/xml_security.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ def algorithm(element)
algorithm = algorithm && algorithm =~ /(rsa-)?sha(.*?)$/i && ::Regexp.last_match(2).to_i

case algorithm
when 256 then OpenSSL::Digest::SHA256
when 1 then OpenSSL::Digest::SHA1
when 384 then OpenSSL::Digest::SHA384
when 512 then OpenSSL::Digest::SHA512
else
OpenSSL::Digest::SHA1
OpenSSL::Digest::SHA256
end
end

Expand Down Expand Up @@ -114,7 +114,7 @@ def uuid
# <KeyInfo />
# <Object />
# </Signature>
def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_method = SHA1)
def sign_document(private_key, certificate, signature_method = RSA_SHA256, digest_method = SHA256)
noko = Nokogiri::XML(to_s) do |config|
config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
end
Expand Down Expand Up @@ -216,7 +216,7 @@ def validate_document(idp_cert_fingerprint, soft = true, options = {})
if options[:fingerprint_alg]
fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
else
fingerprint_alg = OpenSSL::Digest.new('SHA1')
fingerprint_alg = OpenSSL::Digest.new('SHA256')
end
fingerprint = fingerprint_alg.hexdigest(cert.to_der)

Expand Down
38 changes: 19 additions & 19 deletions test/idp_metadata_parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def initialize; end
assert_equal "https://hello.example.com/access/saml/idp.xml", settings.idp_entity_id
assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_service_url
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", settings.idp_sso_service_binding
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.idp_cert_fingerprint
assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_service_url
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", settings.idp_slo_service_binding
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", settings.name_identifier_format
Expand All @@ -38,15 +38,15 @@ def initialize; end
idp_metadata_parser = RubySaml::IdpMetadataParser.new
idp_metadata = idp_metadata_descriptor
settings = idp_metadata_parser.parse(idp_metadata)
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.idp_cert_fingerprint
end

it "extract certificate from md:KeyDescriptor[@use='encryption']" do
idp_metadata_parser = RubySaml::IdpMetadataParser.new
idp_metadata = idp_metadata_descriptor
idp_metadata = idp_metadata.sub(/<md:KeyDescriptor use="signing">(.*?)<\/md:KeyDescriptor>/m, "")
settings = idp_metadata_parser.parse(idp_metadata)
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.idp_cert_fingerprint
end

it "extract certificate from md:KeyDescriptor" do
Expand All @@ -55,7 +55,7 @@ def initialize; end
idp_metadata = idp_metadata.sub(/<md:KeyDescriptor use="signing">(.*?)<\/md:KeyDescriptor>/m, "")
idp_metadata = idp_metadata.sub('<md:KeyDescriptor use="encryption">', '<md:KeyDescriptor>')
settings = idp_metadata_parser.parse(idp_metadata)
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.idp_cert_fingerprint
end

it "extract SSO endpoint with no specific binding, it takes the first" do
Expand Down Expand Up @@ -162,7 +162,7 @@ def initialize; end
}
}
})
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.idp_cert_fingerprint
assert_equal XMLSecurity::Document::SHA256, settings.security[:digest_method]
assert_equal XMLSecurity::Document::RSA_SHA256, settings.security[:signature_method]
end
Expand All @@ -175,7 +175,7 @@ def initialize; end

RubySaml::IdpMetadataParser.new.parse(idp_metadata_descriptor, :settings => settings)

assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.idp_cert_fingerprint
assert_equal XMLSecurity::Document::SHA256, settings.security[:digest_method]
assert_equal XMLSecurity::Document::RSA_SHA256, settings.security[:signature_method]
end
Expand All @@ -190,7 +190,7 @@ def initialize; end
assert_equal "https://hello.example.com/access/saml/idp.xml", metadata[:idp_entity_id]
assert_equal "https://hello.example.com/access/saml/login", metadata[:idp_sso_service_url]
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", metadata[:idp_sso_service_binding]
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", metadata[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", metadata[:idp_cert_fingerprint]
assert_equal "https://hello.example.com/access/saml/logout", metadata[:idp_slo_service_url]
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", metadata[:idp_slo_service_binding]
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", metadata[:name_identifier_format]
Expand All @@ -202,15 +202,15 @@ def initialize; end
idp_metadata_parser = RubySaml::IdpMetadataParser.new
idp_metadata = idp_metadata_descriptor
metadata = idp_metadata_parser.parse_to_hash(idp_metadata)
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", metadata[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", metadata[:idp_cert_fingerprint]
end

it "extract certificate from md:KeyDescriptor[@use='encryption']" do
idp_metadata_parser = RubySaml::IdpMetadataParser.new
idp_metadata = idp_metadata_descriptor
idp_metadata = idp_metadata.sub(/<md:KeyDescriptor use="signing">(.*?)<\/md:KeyDescriptor>/m, "")
parsed_metadata = idp_metadata_parser.parse_to_hash(idp_metadata)
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", parsed_metadata[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", parsed_metadata[:idp_cert_fingerprint]
end

it "extract certificate from md:KeyDescriptor" do
Expand All @@ -219,7 +219,7 @@ def initialize; end
idp_metadata = idp_metadata.sub(/<md:KeyDescriptor use="signing">(.*?)<\/md:KeyDescriptor>/m, "")
idp_metadata = idp_metadata.sub('<md:KeyDescriptor use="encryption">', '<md:KeyDescriptor>')
parsed_metadata = idp_metadata_parser.parse_to_hash(idp_metadata)
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", parsed_metadata[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", parsed_metadata[:idp_cert_fingerprint]
end

it "extract SSO endpoint with no specific binding, it takes the first" do
Expand Down Expand Up @@ -261,7 +261,7 @@ def initialize; end
}
}
})
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", parsed_metadata[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", parsed_metadata[:idp_cert_fingerprint]
assert_nil parsed_metadata[:security]
end

Expand All @@ -272,8 +272,8 @@ def initialize; end
metadata1 = idp_metadata_parser.parse_to_hash(idp_metadata1)
metadata2 = idp_metadata_parser.parse_to_hash(idp_metadata2)

assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", metadata1[:idp_cert_fingerprint]
assert_equal "CD:2B:2B:DA:FF:F5:DB:64:10:7C:AC:FD:FE:0F:CB:5D:73:5F:16:07", metadata2[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", metadata1[:idp_cert_fingerprint]
assert_equal "E5:52:D9:2C:3C:DC:3D:09:5C:90:76:82:AB:B6:75:B4:92:92:2C:42:87:7E:18:EB:17:F3:1F:39:FE:9F:7C:6A", metadata2[:idp_cert_fingerprint]
end
end

Expand Down Expand Up @@ -320,7 +320,7 @@ def initialize; end
assert_equal "https://hello.example.com/access/saml/idp.xml", settings.idp_entity_id
assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_service_url
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", settings.idp_sso_service_binding
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.idp_cert_fingerprint
assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_service_url
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", settings.idp_slo_service_binding
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", settings.name_identifier_format
Expand Down Expand Up @@ -356,7 +356,7 @@ def initialize; end
assert_equal "https://hello.example.com/access/saml/idp.xml", parsed_metadata[:idp_entity_id]
assert_equal "https://hello.example.com/access/saml/login", parsed_metadata[:idp_sso_service_url]
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", parsed_metadata[:idp_sso_service_binding]
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", parsed_metadata[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", parsed_metadata[:idp_cert_fingerprint]
assert_equal "https://hello.example.com/access/saml/logout", parsed_metadata[:idp_slo_service_url]
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", parsed_metadata[:idp_slo_service_binding]
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", parsed_metadata[:name_identifier_format]
Expand Down Expand Up @@ -467,7 +467,7 @@ def initialize; end
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", @settings.name_identifier_format
assert_equal "https://hello.example.com/access/saml/login", @settings.idp_sso_service_url
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", @settings.idp_sso_service_binding
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", @settings.idp_cert_fingerprint
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", @settings.idp_cert_fingerprint
assert_equal "https://hello.example.com/access/saml/logout", @settings.idp_slo_service_url
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", @settings.idp_slo_service_binding
assert_equal ["AuthToken", "SSOStartPage"], @settings.idp_attribute_names
Expand All @@ -477,10 +477,10 @@ def initialize; end
it "should handle multiple descriptors at once" do
settings = @idp_metadata_parser.parse_to_array(@idp_metadata)
assert_equal "https://foo.example.com/access/saml/idp.xml", settings.first[:idp_entity_id]
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.first[:idp_cert_fingerprint]
assert_equal "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21", settings.first[:idp_cert_fingerprint]
assert_equal '2014-04-17T18:02:33.910Z', settings.first[:valid_until]
assert_equal "https://bar.example.com/access/saml/idp.xml", settings.last[:idp_entity_id]
assert_equal "08:EB:6E:60:A2:14:4E:89:EC:FA:05:74:9D:72:BF:5D:BE:54:F0:1A", settings.last[:idp_cert_fingerprint]
assert_equal "74:E4:FA:29:20:26:36:8A:72:5E:9D:CF:4F:8E:1F:DC:D4:CE:E2:3C:9D:6F:93:35:A1:A7:8A:4D:79:83:21:D0", settings.last[:idp_cert_fingerprint]
assert_equal '2014-04-17T18:02:33.910Z', settings.last[:valid_until]
end
end
Expand Down Expand Up @@ -649,7 +649,7 @@ def initialize; end

it "should return idp_cert and idp_cert_fingerprint and no idp_cert_multi" do
assert_equal(expected_cert, @settings.idp_cert)
assert_equal("2D:A9:40:88:28:EE:67:BB:4A:5B:E0:58:A7:CC:71:95:2D:1B:C9:D3", @settings.idp_cert_fingerprint)
assert_equal("46:E3:68:F4:ED:61:43:2B:EC:36:E3:99:E9:03:4B:99:E5:B3:58:EF:A9:A9:00:FC:2D:C8:7C:14:C6:60:E3:8F", @settings.idp_cert_fingerprint)
assert_equal({ :signing => [expected_cert], :encryption => [expected_cert] }, @settings.idp_cert_multi)
assert_equal("https://app.onelogin.com/saml/metadata/383123", @settings.idp_entity_id)
assert_equal("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", @settings.name_identifier_format)
Expand Down
Loading

0 comments on commit 38e3678

Please sign in to comment.