Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ jobs:
- 3.3
- 3.4
- jruby-9.4
- jruby-10.0
- truffleruby
exclude:
- os: windows-latest
ruby-version: jruby-9.4
- os: windows-latest
ruby-version: jruby-10.0
- os: windows-latest
ruby-version: truffleruby
runs-on: ${{ matrix.os }}
Expand Down
17 changes: 11 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
* [#731](https://github.com/SAML-Toolkits/ruby-saml/pull/731) Add CI coverage for Ruby 3.4. Remove CI coverage for Ruby 1.x and 2.x.
* [#735](https://github.com/SAML-Toolkits/ruby-saml/pull/735) Add `Settings#sp_uuid_prefix` and deprecate `Utils#set_prefix`.

### 1.18.1 (Jul 29, 2025)
* Fix vulnerability CVE-2025-54572 Prevent DOS due large SAML Message
* Adapt tests to be able to execute signature validation sooner
* CI Improvements. Support Ruby 3.4

### 1.18.0 (Mar 12, 2025)
* [#750](https://github.com/SAML-Toolkits/ruby-saml/pull/750) Fix vulnerabilities: CVE-2025-25291, CVE-2025-25292: SAML authentication bypass via Signature Wrapping attack allowed due parser differential. Fix vulnerability: CVE-2025-25293: Potential DOS abusing of compressed messages.
* [#718](https://github.com/SAML-Toolkits/ruby-saml/pull/718/) Add support to retrieve from SAMLResponse the AuthnInstant and AuthnContextClassRef values
Expand Down Expand Up @@ -61,7 +66,7 @@
* [#614](https://github.com/SAML-Toolkits/ruby-saml/pull/614) Support :name_id_format option for IdpMetadataParser
* [#611](https://github.com/SAML-Toolkits/ruby-saml/pull/611) IdpMetadataParser should always set idp_cert_multi, even when there is only one cert
* [#610](https://github.com/SAML-Toolkits/ruby-saml/pull/610) New IDP sso/slo binding params which deprecate :embed_sign
* [#602](https://github.com/SAML-Toolkits/ruby-saml/pull/602) Refactor the OneLogin::RubySaml::Metadata class
* [#602](https://github.com/SAML-Toolkits/ruby-saml/pull/602) Refactor the RubySaml::Metadata class
* [#586](https://github.com/SAML-Toolkits/ruby-saml/pull/586) Support milliseconds in cacheDuration parsing
* [#585](https://github.com/SAML-Toolkits/ruby-saml/pull/585) Do not append " | " to StatusCode unnecessarily
* [#607](https://github.com/SAML-Toolkits/ruby-saml/pull/607) Clean up
Expand Down Expand Up @@ -136,7 +141,7 @@
* Updated invalid audience error message

### 1.7.2 (Feb 28, 2018)
* [#446](https://github.com/SAML-Toolkits/ruby-saml/pull/446) Normalize text returned by OneLogin::RubySaml::Utils.element_text
* [#446](https://github.com/SAML-Toolkits/ruby-saml/pull/446) Normalize text returned by RubySaml::Utils.element_text

### 1.7.1 (Feb 28, 2018)
* [#444](https://github.com/SAML-Toolkits/ruby-saml/pull/444) Fix audience validation for empty audience restriction
Expand Down Expand Up @@ -266,7 +271,7 @@
* [#226](https://github.com/SAML-Toolkits/ruby-saml/pull/226) Ensure IdP certificate is formatted properly
* [#225](https://github.com/SAML-Toolkits/ruby-saml/pull/225) Add documentation to several methods. Fix xpath injection on xml_security.rb
* [#223](https://github.com/SAML-Toolkits/ruby-saml/pull/223) Allow logging to be delegated to an arbitrary Logger
* [#222](https://github.com/SAML-Toolkits/ruby-saml/pull/222) No more silent failure fetching idp metadata (OneLogin::RubySaml::HttpError raised).
* [#222](https://github.com/SAML-Toolkits/ruby-saml/pull/222) No more silent failure fetching idp metadata (RubySaml::HttpError raised).

### 0.9.2 (Apr 28, 2015)
* [#216](https://github.com/SAML-Toolkits/ruby-saml/pull/216) Add fingerprint algorithm support
Expand Down Expand Up @@ -314,10 +319,10 @@
* [#183](https://github.com/SAML-Toolkits/ruby-saml/pull/183) Resolved a security vulnerability where string interpolation in a `REXML::XPath.first()` method call allowed for arbitrary code execution.

### 0.8.0 (Feb 21, 2014)
**IMPORTANT**: This release changed namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
**IMPORTANT**: This release changed namespace of the gem from `Saml` to `RubySaml`. Please update your implementations of the gem accordingly.

* [#111](https://github.com/SAML-Toolkits/ruby-saml/pull/111) `Onelogin::` is `OneLogin::`
* [#108](https://github.com/SAML-Toolkits/ruby-saml/pull/108) Change namespacing from `Onelogin::Saml` to `Onelogin::Rubysaml`
* [#111](https://github.com/SAML-Toolkits/ruby-saml/pull/111) `` is ``
* [#108](https://github.com/SAML-Toolkits/ruby-saml/pull/108) Change namespacing from `Saml` to `Rubysaml`

### 0.7.3 (Feb 20, 2014)
Updated gem dependencies to be compatible with Ruby 1.8.7-p374 and 1.9.3-p448. Removed unnecessary `canonix` gem dependency.
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,18 @@ Ruby SAML minor versions may introduce breaking changes. Please read

## Vulnerability Notice

There are **critical vulnerabilities** affecting ruby-saml < 1.18.0 which allow
SAML authentication bypass (CVE-2024-45409, CVE-2025-25291, CVE-2025-25292, CVE-2025-25293).
**Please upgrade to a fixed version (1.18.0 or 2.0.0) as soon as possible.**
Please note the following **critical vulnerabilities**:

- CVE-2025-54572 (DOS attack vector) affects version ruby-saml < 1.18.1
- CVE-2024-45409, CVE-2025-25291, CVE-2025-25292, CVE-2025-25293 (SAML authentication bypass) affects ruby-saml < 1.18.0

**Please upgrade to a fixed version (2.0.0 or 1.18.1) as soon as possible.**

## Sponsors

Thanks to the following sponsors for securing the open source ecosystem,

[<img alt="84codes" src="https://avatars.githubusercontent.com/u/5353257" width="75px">](https://www.84codes.com)

## Overview

Expand Down Expand Up @@ -46,7 +55,7 @@ it by email to the maintainer: sixto.martin.garcia+security@gmail.com
The following Ruby versions are covered by CI testing:

* Ruby (MRI) 3.0 to 3.4
* JRuby 9.4
* JRuby 9.4 to 10.0
* TruffleRuby (latest)

Older Ruby versions are supported on the 1.x release of Ruby SAML.
Expand Down
10 changes: 5 additions & 5 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ This issue is likely not critical for most IdPs, but since it is not tested, it

### Root "OneLogin" namespace changed to "RubySaml"

RubySaml version `2.0.0` changes the root namespace from `OneLogin::RubySaml::` to just `RubySaml::`.
Please remove `OneLogin::` and `onelogin/` everywhere in your codebase. Aside from this namespace change,
RubySaml version `2.0.0` changes the root namespace from `RubySaml::` to just `RubySaml::`.
Please remove `` and `onelogin/` everywhere in your codebase. Aside from this namespace change,
the class names themselves have intentionally been kept the same.

Note that the project folder structure has also been updated accordingly. Notably, the directory
`lib/onelogin/schemas` is now `lib/ruby_saml/schemas`.

For backward compatibility, the alias `OneLogin = Object` has been set, so `OneLogin::RubySaml::` will still work
For backward compatibility, the alias `OneLogin = Object` has been set, so `RubySaml::` will still work
as before. This alias will be removed in RubySaml version `3.0.0`.

### Deprecation and removal of "XMLSecurity" namespace
Expand Down Expand Up @@ -319,7 +319,7 @@ options = {
"RelayState" => raw_query_params["RelayState"],
},
}
slo_logout_request = OneLogin::RubySaml::SloLogoutrequest.new(query_params["SAMLRequest"], settings, options)
slo_logout_request = RubySaml::SloLogoutrequest.new(query_params["SAMLRequest"], settings, options)
raise "Invalid Logout Request" unless slo_logout_request.is_valid?
```

Expand Down Expand Up @@ -379,4 +379,4 @@ Version `0.9` adds many new features and improvements.

## Upgrading from 0.7.x to 0.8.x

Version `0.8.x` changes the namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
Version `0.8.x` changes the namespace of the gem from `Saml` to `RubySaml`. Please update your implementations of the gem accordingly.
70 changes: 69 additions & 1 deletion test/response_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class RubySamlTest < Minitest::Test
describe "Response" do
let(:settings) { RubySaml::Settings.new }
let(:response) { RubySaml::Response.new(response_document_without_recipient) }
let(:response_without_recipient) { OneLogin::RubySaml::Response.new(signed_response_document_without_recipient) }
let(:response_without_recipient) { RubySaml::Response.new(signed_response_document_without_recipient) }
let(:response_without_attributes) { RubySaml::Response.new(response_document_without_attributes) }
let(:response_with_multiple_attribute_statements) { RubySaml::Response.new(fixture(:response_with_multiple_attribute_statements)) }
let(:response_without_reference_uri) { RubySaml::Response.new(response_document_without_reference_uri) }
Expand Down Expand Up @@ -1752,6 +1752,74 @@ def generate_audience_error(expected, actual)
end
end

describe "DOS attack via oversized payload" do
it "rejects oversized payloads before attempting Base64 validation" do
large_saml_response = "A" * (RubySaml::Settings::DEFAULTS[:message_max_bytesize] + 100)

assert_raises(RubySaml::ValidationError, "Encoded SAML Message exceeds #{RubySaml::Settings::DEFAULTS[:message_max_bytesize]} bytes, so was rejected") do
RubySaml::Response.new(large_saml_response)
end
end

it "rejects oversized payloads before attempting Base64 validation with custom max_bytesize" do
custom_max = 10000
large_saml_response = "A" * (custom_max + 100)
custom_settings = RubySaml::Settings.new({ message_max_bytesize: custom_max })

assert_raises(RubySaml::ValidationError, "Encoded SAML Message exceeds #{custom_max} bytes, so was rejected") do
RubySaml::Response.new(large_saml_response, settings: custom_settings)
end
end
end

describe "Zlib bomb attack" do
it "rejects Zlib bomb attacks" do
# Create a message that when inflated would be extremely large
bomb_prefix = <<~XML
<?xml version='1.0' encoding='UTF-8'?>
<samlp:Response xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion' ID='_test_response_id' Version='2.0' IssueInstant='2014-07-18T01:13:06Z' Destination='http://sp.example.com/saml/acs'>
<saml:Issuer>http://idp.example.com/</saml:Issuer>
<samlp:Status><samlp:StatusCode Value='urn:oasis:names:tc:SAML:2.0:status:Success'/></samlp:Status>
<saml:Assertion xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion' ID='_test_assertion_id' IssueInstant='2014-07-18T01:13:06Z' Version='2.0'>
<saml:Issuer>http://idp.example.com/</saml:Issuer>
<saml:Subject><saml:NameID>
XML

bomb_suffix = <<~XML
</saml:NameID></saml:Subject>
</saml:Assertion>
</samlp:Response>
XML

bomb_data = bomb_prefix + 'A' * (200_000 * 1024) + bomb_suffix
bomb = Base64.strict_encode64(Zlib::Deflate.deflate(bomb_data, 9)[2..-5])

assert_raises(RubySaml::ValidationError) do
RubySaml::Response.new(bomb)
end
end

it "rejects Zlib bomb attacks with custom max_bytesize" do
custom_max = 100_000
custom_settings = RubySaml::Settings.new({:message_max_bytesize => custom_max})

bomb_prefix = <<~XML
<?xml version='1.0' encoding='UTF-8'?>
<samlp:Response xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' ID='_test' Version='2.0'>
<saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>http://idp.example.com/</saml:Issuer>
XML

bomb_suffix = "</samlp:Response>"

bomb_data = bomb_prefix + 'A' * (custom_max + 100) + bomb_suffix
bomb = Base64.strict_encode64(Zlib::Deflate.deflate(bomb_data, 9)[2..-5])

assert_raises(RubySaml::ValidationError, "SAML Message exceeds #{custom_max} bytes, so was rejected") do
RubySaml::Response.new(bomb, settings: custom_settings)
end
end
end

describe "signature wrapping attack with encrypted assertion" do
it "should not be valid" do
settings.private_key = ruby_saml_key_text
Expand Down
23 changes: 23 additions & 0 deletions test/xml/decoder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,29 @@ class XmlDecoderTest < Minitest::Test
end
end

it 'rejects oversized payloads before attempting Base64 validation' do
large_saml_message = 'A' * (RubySaml::XML::Decoder::DEFAULT_MAX_BYTESIZE + 100)

assert_raises(RubySaml::ValidationError, "Encoded SAML Message exceeds #{RubySaml::XML::Decoder::DEFAULT_MAX_BYTESIZE} bytes, so was rejected") do
# Mock to ensure base64_encoded? is never called on oversized input
RubySaml::XML::Decoder.expects(:base64_encoded?).never

RubySaml::XML::Decoder.decode_message(large_saml_message)
end
end

it 'rejects oversized payloads before attempting Base64 validation with custom max' do
custom_max = 1000
large_saml_message = 'A' * (custom_max + 100)

assert_raises(RubySaml::ValidationError, "Encoded SAML Message exceeds #{custom_max} bytes, so was rejected") do
# Mock to ensure base64_encoded? is never called on oversized input
RubySaml::XML::Decoder.expects(:base64_encoded?).never

RubySaml::XML::Decoder.decode_message(large_saml_message, custom_max)
end
end

it "rejects Zlib bomb attacks" do
# Create a message that when inflated would be extremely large
bomb_prefix = <<~XML
Expand Down