Skip to content

Commit 0f8c3d1

Browse files
benjaminwolsdblessingpitbulk
authored
Merge upstream (#4)
* Check message bytesize before Base64 validation * Adapt tests to be able to execute signature validation sooner * CI/CD: Update ubuntu version * Add Ruby 3.4 to the CI * Exclude Ubuntu-22.04 Ruby 2.2 from CI as it raises RubyGem/Bundler issues * Release 1.18.1 * Update Changelog * Fix several typos on the documentation and code. Add to the README how to force SP-Initiate flow and Prevent Reply Attacks * Improve the inflate method to prevent potential DoS vulnerability in Zlib::Inflate (SAML-Toolkits#779) Improve the inflate method. Prevent potential DoS vulnerability in Zlib::Inflate by limiting the maximum decompressed size. The data is now inflated in chunks. * Add sponsors: Github, SerpApi * Add warning about CVE-2025-66567 and CVE-2025-66568 CVE-2025-66567 and CVE-2025-66568 affects ruby-saml <= 1.12.4. Use ruby-saml 1.18.1 instead. * Revise vulnerability notice for ruby-saml Updated vulnerability notice to reflect affected versions. * Fix wrong link --------- Co-authored-by: Drew Blessing <drew@blessing.io> Co-authored-by: Sixto Martin <sixto.martin.garcia@workato.com> Co-authored-by: Sixto Martin <sixto.martin.garcia@gmail.com> Co-authored-by: Sixto Martin <pitbulk@gmail.com>
1 parent fddd7de commit 0f8c3d1

23 files changed

+335
-133
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
fail-fast: false
1010
matrix:
1111
os:
12-
- ubuntu-20.04
12+
- ubuntu-22.04
1313
- macos-latest
1414
- windows-latest
1515
ruby-version:
@@ -24,6 +24,7 @@ jobs:
2424
- 3.1
2525
- 3.2
2626
- 3.3
27+
- 3.4
2728
- jruby-9.1
2829
- jruby-9.2
2930
- jruby-9.3
@@ -56,6 +57,8 @@ jobs:
5657
ruby-version: jruby-9.4
5758
- os: windows-latest
5859
ruby-version: truffleruby
60+
- os: ubuntu-22.04
61+
ruby-version: 2.2
5962
runs-on: ${{ matrix.os }}
6063
steps:
6164
- uses: actions/checkout@v4

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Ruby SAML Changelog
22

3+
### 1.18.1 (Jul 29, 2025)
4+
* Fix vulnerability CVE-2025-54572 Prevent DOS due large SAML Message
5+
* Adapt tests to be able to execute signature validation sooner
6+
* CI Improvements. Support Ruby 3.4
7+
38
### 1.18.0 (Mar 12, 2025)
49
* [#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.
510
* [#718](https://github.com/SAML-Toolkits/ruby-saml/pull/718/) Add support to retrieve from SAMLResponse the AuthnInstant and AuthnContextClassRef values

README.md

Lines changed: 193 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,33 @@
77
Minor and patch versions of Ruby SAML may introduce breaking changes. Please read
88
[UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions.
99

10-
### Pay it Forward: Support RubySAML and Strengthen Open-Source Security
1110

12-
RubySAML is a trusted authentication library used by startups and enterprises alike.
11+
## Vulnerability Notice
1312

14-
But security doesn't happen in a vacuum. Vulnerabilities in authentication libraries can
15-
have widespread consequences. Maintaining open-source security requires continuous
16-
effort, expertise, and funding. By supporting RubySAML, you’re not just securing your
17-
own systems—you’re strengthening auth security globally.
13+
[CVE-2025-66568](https://github.com/SAML-Toolkits/ruby-saml/security/advisories/GHSA-x4h9-gwv3-r4m4) and [CVE-2025-66567](https://github.com/SAML-Toolkits/ruby-saml/security/advisories/GHSA-9v8j-x534-2fx3). affects version ruby-saml < 1.18.0 (including 1.12.4), upgrade to 1.18.1
1814

19-
#### How you can help
15+
CVE-2025-54572 affects version ruby-saml < 1.18.1
2016

21-
* Sponsor RubySAML: [GitHub Sponsors](https://github.com/sponsors/SAML-Toolkits)
22-
* Contribute to secure-by-design improvements
23-
* Responsibly report vulnerabilities (see "Vulnerability Reporting" above)
17+
There are critical vulnerabilities affecting ruby-saml < 1.18.0, two of them allows SAML authentication bypass (CVE-2025-25291, CVE-2025-25292, CVE-2025-25293). Please upgrade to a fixed version (1.18.0)
2418

25-
Security is a shared responsibility. If RubySAML has helped your organization, please
26-
consider giving back. Together, we can keep authentication secure.
19+
### Vulnerability Reporting
2720

28-
### Sponsors
21+
If you believe you have discovered a security vulnerability in this gem, please report
22+
it by email to the maintainer: sixto.martin.garcia+security@gmail.com
2923

30-
Thanks to the following sponsors for securing the open source ecosystem,
3124

32-
[<img alt="84codes" src="https://avatars.githubusercontent.com/u/5353257" width="75px">](https://www.84codes.com)
25+
## Sponsors
3326

27+
Thanks to the following sponsors for securing the open source ecosystem:
3428

35-
## Vulnerabilities
29+
#### [<img class="circle" src="https://avatars.githubusercontent.com/u/34724717" width="26" height="26" alt="@serpapi">](https://serpapi.com) [<sup>SerpApi</sup>](https://github.com/serpapi)
30+
<sup>*A real-time API to access Google search results. It handle proxies, solve captchas, and parse all rich structured data for you*</sup>
3631

37-
There are critical vulnerabilities affecting ruby-saml < 1.18.0, two of them allows SAML authentication bypass (CVE-2025-25291, CVE-2025-25292, CVE-2025-25293). Please upgrade to a fixed version (1.18.0)
32+
#### [<img class="circle" src="https://avatars.githubusercontent.com/u/9919" width="26" height="26" alt="@github">](https://github.com/) [<sup>Github</sup>](https://github.com/github)
33+
<sup>*The complete developer platform to build, scale, and deliver secure software.*</sup>
34+
35+
#### [<img alt="84codes" src="https://avatars.githubusercontent.com/u/5353257" width="26" height="26">](https://www.84codes.com) [<sup>84codes</sup>](https://github.com/84codes)
36+
<sup>*Simplifying Message Queuing and Streaming. Leave server management to the experts, so you can focus on building great applications.*</sup>
3837

3938
## Overview
4039

@@ -47,46 +46,30 @@ SAML authorization is a two-step process and you are expected to implement suppo
4746
We created a demo project for Rails 4 that uses the latest version of this library:
4847
[ruby-saml-example](https://github.com/saml-toolkits/ruby-saml-example)
4948

50-
### Supported Ruby Versions
5149

52-
The following Ruby versions are covered by CI testing:
50+
### Security Considerations
5351

54-
* Ruby (MRI) 2.1 to 3.3
55-
* JRuby 9.1 to 9.4
56-
* TruffleRuby (latest)
57-
58-
## Adding Features, Pull Requests
59-
60-
* Fork the repository
61-
* Make your feature addition or bug fix
62-
* Add tests for your new features. This is important so we don't break any features in a future version unintentionally.
63-
* Ensure all tests pass by running `bundle exec rake test`.
64-
* Do not change Rakefile, version, or history.
65-
* Open a pull request, following [this template](https://gist.github.com/Lordnibbler/11002759).
66-
67-
## Security Guidelines
52+
- **Validation of the IdP Metadata URL:** When loading IdP Metadata from a URLs,
53+
Ruby SAML requires you (the developer/administrator) to ensure the supplied URL is correct
54+
and from a trusted source. Ruby SAML does not perform any validation that the URL
55+
you entered is correct and/or safe.
56+
- **False-Positive Security Warnings:** Some tools may incorrectly report Ruby SAML as a
57+
potential security vulnerability, due to its dependency on Nokogiri. Such warnings can
58+
be ignored; Ruby SAML uses Nokogiri in a safe way, by always disabling its DTDLOAD option
59+
and enabling its NONET option.
60+
- **Prevent Replay attacks:** A replay attack is when an attacker intercepts a valid SAML
61+
assertion and "replays" it at a later time to gain unauthorized access. The `ruby-saml`
62+
library provides the tools to prevent this, but **you, the developer, must implement thecore logic**, see an specific section later in the README.
6863

69-
If you believe you have discovered a security vulnerability in this gem, please report it
70-
by mail to the maintainer: sixto.martin.garcia+security@gmail.com
7164

72-
### Security Warning
73-
74-
Some tools may incorrectly report ruby-saml is a potential security vulnerability.
75-
ruby-saml depends on Nokogiri, and it is possible to use Nokogiri in a dangerous way
76-
(by enabling its DTDLOAD option and disabling its NONET option).
77-
This dangerous Nokogiri configuration, which is sometimes used by other components,
78-
can create an XML External Entity (XXE) vulnerability if the XML data is not trusted.
79-
However, ruby-saml never enables this dangerous Nokogiri configuration;
80-
ruby-saml never enables DTDLOAD, and it never disables NONET.
65+
### Supported Ruby Versions
8166

82-
The OneLogin::RubySaml::IdpMetadataParser class does not validate the provided URL before parsing.
67+
The following Ruby versions are covered by CI testing:
8368

84-
Usually, the same administrator who handles the Service Provider also sets the URL to
85-
the IdP, which should be a trusted resource.
69+
* Ruby (MRI) 2.1 to 3.4
70+
* JRuby 9.1 to 9.4
71+
* TruffleRuby (latest)
8672

87-
But there are other scenarios, like a SaaS app where the administrator of the app
88-
delegates this functionality to other users. In this case, extra precautions should
89-
be taken in order to validate such URL inputs and avoid attacks like SSRF.
9073

9174
## Getting Started
9275

@@ -429,8 +412,8 @@ Those return an Hash instead of a `Settings` object, which may be useful for con
429412
430413
### Validating Signature of Metadata and retrieve settings
431414
432-
Right now there is no method at ruby_saml to validate the signature of the metadata that gonna be parsed,
433-
but it can be done as follows:
415+
Right now there is no method at ruby_saml to validate the signature of the metadata that is going to be parsed, but it can be done as follows:
416+
434417
* Download the XML.
435418
* Validate the Signature, providing the cert.
436419
* Provide the XML to the parse method if the signature was validated
@@ -719,7 +702,7 @@ SP Metadata XML, to be read by the IdP.
719702
#### Verifying Signature on IdP Assertions
720703
721704
You may require the IdP to sign its SAML Assertions using the following setting.
722-
With will add `<md:SPSSODescriptor WantAssertionsSigned="true">` to your SP Metadata XML.
705+
This will add `<md:SPSSODescriptor WantAssertionsSigned="true">` to your SP Metadata XML.
723706
The signature will be checked against the `<md:KeyDescriptor use="signing">` element
724707
present in the IdP's metadata.
725708
@@ -751,7 +734,7 @@ advanced usage scenarios:
751734
- Specifying separate SP certificates for signing and encryption.
752735
753736
The `sp_cert_multi` parameter replaces `certificate` and `private_key`
754-
(you may not specify both pparameters at the same time.) `sp_cert_multi` has the following shape:
737+
(you may not specify both parameters at the same time.) `sp_cert_multi` has the following shape:
755738
756739
```ruby
757740
settings.sp_cert_multi = {
@@ -821,7 +804,7 @@ def sp_logout_request
821804
settings = saml_settings
822805
823806
if settings.idp_slo_service_url.nil?
824-
logger.info "SLO IdP Endpoint not found in settings, executing then a normal logout'"
807+
logger.info "SLO IdP Endpoint not found in settings, then executing a normal logout'"
825808
delete_session
826809
else
827810
@@ -1004,3 +987,158 @@ end
1004987
# Output XML with custom metadata
1005988
MyMetadata.new.generate(settings)
1006989
```
990+
991+
### Preventing Replay Attacks
992+
993+
A replay attack is when an attacker intercepts a valid SAML assertion and "replays" it at a later time to gain unauthorized access.
994+
995+
The library only checks the assertion's validity window (`NotBefore` and `NotOnOrAfter` conditions). An attacker can replay a valid assertion as many times as they want within this window.
996+
997+
A robust defense requires tracking of assertion IDs to ensure any given assertion is only accepted once.
998+
999+
#### 1. Extract the Assertion ID after Validation
1000+
1001+
After a response has been successfully validated, get the assertion ID. The library makes this available via `response.assertion_id`.
1002+
1003+
1004+
#### 2. Store the ID with an Expiry
1005+
1006+
You must store this ID in a persistent cache (like Redis or Memcached) that is shared across your servers. Do not store it in the user's session, as that is not a secure cache.
1007+
1008+
The ID should be stored until the assertion's validity window has passed. You will need to check how long the trusted IdPs consider the assertion valid and then add the allowed_clock_drift.
1009+
1010+
You can define a global value, or set this value dinamically based on the `not_on_or_after` value of the re + `allowed_clock_drift`.
1011+
1012+
```ruby
1013+
# In your `consume` action, after a successful validation:
1014+
if response.is_valid?
1015+
# Prevent replay of this specific assertion
1016+
assertion_id = response.assertion_id
1017+
authorize_failure("Assertion ID is mandatory") if assertion_id.nil?
1018+
1019+
assertion_not_on_or_after = response.not_on_or_after
1020+
# We set a default of 5 min expiration in case is not provided
1021+
assertion_expiry = (Time.now.utc + 300) if assertion_not_on_or_after.nil?
1022+
1023+
# `is_new_assertion?` is your application's method to check and set the ID
1024+
# in a shared, persistent cache (e.g., Redis, Memcached).
1025+
if is_new_assertion?(assertion_id, expires_at: assertion_expiry)
1026+
# This is a new assertion, so we can proceed
1027+
session[:userid] = response.nameid
1028+
session[:attributes] = response.attributes
1029+
# ...
1030+
else
1031+
# This assertion ID has been seen before. This is a REPLAY ATTACK.
1032+
# Log the security event and reject the user.
1033+
authorize_failure("Replay attack detected")
1034+
end
1035+
else
1036+
authorize_failure("Invalid response")
1037+
end
1038+
```
1039+
1040+
Your `is_new_assertion?` method would look something like this (example for Redis):
1041+
1042+
```ruby
1043+
1044+
def is_new_assertion?(assertion_id, expires_at)
1045+
ttl = (expires_at - Time.now.utc).to_i
1046+
return false if ttl <= 0 # The assertion has already expired
1047+
1048+
# The 'nx' option tells Redis to only set the key if it does not already exist.
1049+
# The command returns `true` if the key was set, `false` otherwise.
1050+
$redis.set("saml_assertion_ids:#{assertion_id}", "1", ex: ttl, nx: true)
1051+
end
1052+
```
1053+
1054+
### Enforce SP-Initiated Flow with `InResponseTo` validation
1055+
1056+
This is the best way to prevent IdP-initiated logins and ensure that you only accept assertions that you recently requested.
1057+
1058+
#### 1. Store the `AuthnRequest` ID
1059+
1060+
When you create an `AuthnRequest`, the library assigns it a unique ID. You must store this ID, for example in the user's session *before* redirecting them to the IdP.
1061+
1062+
```ruby
1063+
def init
1064+
request = OneLogin::RubySaml::Authrequest.new
1065+
# The unique ID of the request is in request.uuid
1066+
session[:saml_request_id] = request.uuid
1067+
redirect_to(request.create(saml_settings))
1068+
end
1069+
```
1070+
1071+
#### 2. Validate the `InResponseTo` value of the `Response` with the Stored ID
1072+
1073+
When you process the `SAMLResponse`, retrieve the ID from the session and pass it to the `Response` constructor. Use `session.delete` to ensure the ID can only be used once.
1074+
1075+
```ruby
1076+
def consume
1077+
request_id = session.delete(:saml_request_id) # Use delete to prevent re-use
1078+
1079+
# You can reject the response if no previous saml_request_id was stored
1080+
raise "IdP-initiaited detected" if request_id.nil?
1081+
1082+
response = OneLogin::RubySaml::Response.new(
1083+
params[:SAMLResponse],
1084+
settings: saml_settings,
1085+
matches_request_id: request_id
1086+
)
1087+
1088+
if response.is_valid?
1089+
# ... authorize user
1090+
else
1091+
# Response is invalid, errors in response.errors
1092+
end
1093+
end
1094+
```
1095+
1096+
## Contributing
1097+
1098+
### Pay it Forward: Support RubySAML and Strengthen Open-Source Security
1099+
1100+
RubySAML is a trusted authentication library used by startups and enterprises alike—
1101+
a community-driven alternative to costly third-party services.
1102+
1103+
But security doesn't happen in a vacuum. Vulnerabilities in authentication libraries can
1104+
have widespread consequences. Maintaining open-source security requires continuous
1105+
effort, expertise, and funding. By supporting RubySAML, you’re not just securing your
1106+
own systems—you’re strengthening auth security globally. Instead of paying for closed
1107+
solutions, consider investing in the community that does the real security work.
1108+
1109+
#### How you can help
1110+
1111+
* Sponsor RubySAML: [GitHub Sponsors](https://github.com/sponsors/SAML-Toolkits)
1112+
* Contribute to secure-by-design improvements
1113+
* Responsibly report vulnerabilities (see "Vulnerability Reporting" above)
1114+
1115+
Security is a shared responsibility. If RubySAML has helped your organization, please
1116+
consider giving back. Together, we can keep authentication secure—without putting it
1117+
behind paywalls.
1118+
1119+
#### Adding Features, Pull Requests
1120+
1121+
* Fork the repository
1122+
* Make your feature addition or bug fix
1123+
* Add tests for your new features. This is important so we don't break any features in a future version unintentionally.
1124+
* Ensure all tests pass by running `bundle exec rake test`.
1125+
* Do not change Rakefile, version, or history.
1126+
* Open a pull request, following [this template](https://gist.github.com/Lordnibbler/11002759).
1127+
1128+
### Sponsors
1129+
1130+
Thanks to the following sponsors for securing the open source ecosystem:
1131+
1132+
#### [<img class="circle" src="https://avatars.githubusercontent.com/u/34724717" width="26" height="26" alt="@serpapi">](https://serpapi.com) [<sup>SerpApi</sup>](https://github.com/serpapi)
1133+
<sup>*A real-time API to access Google search results. It handle proxies, solve captchas, and parse all rich structured data for you*</sup>
1134+
1135+
#### [<img class="circle" src="https://avatars.githubusercontent.com/u/9919" width="26" height="26" alt="@github">](https://github.com/) [<sup>Github</sup>](https://github.com/github)
1136+
<sup>*The complete developer platform to build, scale, and deliver secure software.*</sup>
1137+
1138+
#### [<img alt="84codes" src="https://avatars.githubusercontent.com/u/5353257" width="26" height="26">](https://www.84codes.com) [<sup>84codes</sup>](https://github.com/84codes)
1139+
<sup>*Simplifying Message Queuing and Streaming. Leave server management to the experts, so you can focus on building great applications.*</sup>
1140+
1141+
1142+
## License
1143+
1144+
Ruby SAML is made available under the MIT License. Refer to [LICENSE](LICENSE).

UPGRADING.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ how validation happens in the toolkit and also the toolkit by default will check
77
when parsing a SAML Message (`settings.check_malformed_doc`).
88

99
The SignedDocument class defined at xml_security.rb experienced several changes.
10-
We don't expect compatibilty issues if you use the main methods offered by ruby-saml, but if you use a fork or customized usage, is possible that you need to adapt your code.
10+
We don't expect compatibility issues if you use the main methods offered by ruby-saml, but if you use a fork or customized usage, is possible that you need to adapt your code.
1111

1212
## Updating from 1.12.x to 1.13.0
1313

@@ -33,7 +33,7 @@ in favor of `idp_sso_service_url` and `idp_slo_service_url`. The `IdpMetadataPar
3333

3434
## Upgrading from 1.10.x to 1.11.0
3535

36-
Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
36+
Version `1.11.0` deprecates the use of `settings.issuer` in favor of `settings.sp_entity_id`.
3737
There are two new security settings: `settings.security[:check_idp_cert_expiration]` and
3838
`settings.security[:check_sp_cert_expiration]` (both false by default) that check if the
3939
IdP or SP X.509 certificate has expired, respectively.
@@ -44,7 +44,7 @@ Version `1.10.1` improves Ruby 1.8.7 support.
4444

4545
## Upgrading from 1.9.0 to 1.10.0
4646

47-
Version `1.10.0` improves IdpMetadataParser to allow parse multiple IDPSSODescriptor,
47+
Version `1.10.0` improves IdpMetadataParser to allow parsing multiple IDPSSODescriptor,
4848
Add Subject support on AuthNRequest to allow SPs provide info to the IdP about the user
4949
to be authenticated and updates the format_cert method to accept certs with /\x0d/
5050

@@ -128,7 +128,7 @@ It adds security improvements in order to prevent Signature wrapping attacks.
128128

129129
## Upgrading from 1.1.x to 1.2.x
130130

131-
Version `1.2` adds IDP metadata parsing improvements, uuid deprecation in favour of SecureRandom,
131+
Version `1.2` adds IDP metadata parsing improvements, uuid deprecation in favor of SecureRandom,
132132
refactor error handling and some minor improvements.
133133

134134
There is no compatibility issue detected.
@@ -143,7 +143,7 @@ Version `1.1` adds some improvements on signature validation and solves some nam
143143

144144
Version `1.0` is a recommended update for all Ruby SAML users as it includes security fixes.
145145

146-
Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decrypt support.
146+
Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decryption support.
147147

148148
### Important Changes
149149

0 commit comments

Comments
 (0)