Skip to content

Commit 5d3b53e

Browse files
Replace MAX_BYTE_SIZE constant with setting
The MAX_BYTE_SIZE constant did not allow for customization, which is necessary for cases where legitimate SAML responses are larger than 250,000 bytes. This replaces the constant with a setting, which has a default value of 250,000 bytes, but can be customized like any other setting.
1 parent b55733f commit 5d3b53e

10 files changed

Lines changed: 51 additions & 30 deletions

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,28 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :allowed_cloc
804804
805805
Make sure to keep the value as comfortably small as possible to keep security risks to a minimum.
806806
807+
## Deflation Limit
808+
809+
To protect against decompression bombs (a form of DoS attack), SAML messages are limited to 250,000 bytes by default.
810+
Sometimes legitimate SAML messages will exceed this limit,
811+
for example due to custom claims like including groups a user is a member of.
812+
If you want to customize this limit, you need to provide a different setting.
813+
Example:
814+
815+
```ruby
816+
def consume
817+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
818+
response.settings = saml_settings
819+
...
820+
end
821+
822+
private
823+
824+
def saml_settings
825+
OneLogin::RubySaml::Settings.new(message_max_bytesize: 500000)
826+
end
827+
```
828+
807829
## Attribute Service
808830
809831
To request attributes from the IdP the SP needs to provide an attribute service within it's metadata and reference the index in the assertion.

lib/onelogin/ruby-saml/logoutresponse.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class Logoutresponse < SamlMessage
3434
def initialize(response, settings = nil, options = {})
3535
@errors = []
3636
raise ArgumentError.new("Logoutresponse cannot be nil") if response.nil?
37-
@settings = settings
37+
@settings = settings || default_settings
3838

3939
if settings.nil? || settings.soft.nil?
4040
@soft = true
@@ -43,7 +43,7 @@ def initialize(response, settings = nil, options = {})
4343
end
4444

4545
@options = options
46-
@response = decode_raw_saml(response)
46+
@response = decode_raw_saml(response, settings)
4747
@document = XMLSecurity::SignedDocument.new(@response)
4848
end
4949

lib/onelogin/ruby-saml/response.rb

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,11 @@ def initialize(response, options = {})
5353
raise ArgumentError.new("Response cannot be nil") if response.nil?
5454

5555
@errors = []
56-
5756
@options = options
58-
@soft = true
59-
unless options[:settings].nil?
60-
@settings = options[:settings]
61-
unless @settings.soft.nil?
62-
@soft = @settings.soft
63-
end
64-
end
57+
@settings = options[:settings] || default_settings
58+
@soft = @settings.soft || true
6559

66-
@response = decode_raw_saml(response)
60+
@response = decode_raw_saml(response, settings)
6761
@document = XMLSecurity::SignedDocument.new(@response, @errors)
6862

6963
if assertion_encrypted?

lib/onelogin/ruby-saml/saml_message.rb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ class SamlMessage
2222
BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z)
2323
@@mutex = Mutex.new
2424

25-
MAX_BYTE_SIZE = 250000
26-
2725
# @return [Nokogiri::XML::Schema] Gets the schema object of the SAML 2.0 Protocol schema
2826
#
2927
def self.schema
@@ -88,11 +86,11 @@ def valid_saml?(document, soft = true)
8886
# @param saml [String] The deflated and encoded SAML Message
8987
# @return [String] The plain SAML Message
9088
#
91-
def decode_raw_saml(saml)
89+
def decode_raw_saml(saml, settings)
9290
return saml unless base64_encoded?(saml)
9391

94-
if saml.bytesize > MAX_BYTE_SIZE
95-
raise ValidationError.new("Encoded SAML Message exceeds " + MAX_BYTE_SIZE.to_s + " bytes, so was rejected")
92+
if saml.bytesize > settings.message_max_bytesize
93+
raise ValidationError.new("Encoded SAML Message exceeds " + settings.message_max_bytesize.to_s + " bytes, so was rejected")
9694
end
9795

9896
decoded = decode(saml)
@@ -157,6 +155,13 @@ def inflate(deflated)
157155
def deflate(inflated)
158156
Zlib::Deflate.deflate(inflated, 9)[2..-5]
159157
end
158+
159+
# Default settings
160+
# @return [OneLogin::RubySaml::Settings] A settings object
161+
#
162+
def default_settings
163+
OneLogin::RubySaml::Settings.new
164+
end
160165
end
161166
end
162167
end

lib/onelogin/ruby-saml/settings.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def initialize(overrides = {}, keep_security_attributes = false)
5353
attr_accessor :compress_request
5454
attr_accessor :compress_response
5555
attr_accessor :double_quote_xml_attribute_values
56+
attr_accessor :message_max_bytesize
5657
attr_accessor :passive
5758
attr_accessor :protocol_binding
5859
attr_accessor :attributes_index
@@ -258,6 +259,7 @@ def get_sp_key
258259
:idp_cert_fingerprint_algorithm => XMLSecurity::Document::SHA1,
259260
:compress_request => true,
260261
:compress_response => true,
262+
:message_max_bytesize => 250000,
261263
:soft => true,
262264
:double_quote_xml_attribute_values => false,
263265
:security => {

lib/onelogin/ruby-saml/slo_logoutrequest.rb

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,10 @@ def initialize(request, options = {})
3535

3636
@errors = []
3737
@options = options
38-
@soft = true
39-
unless options[:settings].nil?
40-
@settings = options[:settings]
41-
unless @settings.soft.nil?
42-
@soft = @settings.soft
43-
end
44-
end
38+
@settings = options[:settings] || default_settings
39+
@soft = @settings.soft || true
4540

46-
@request = decode_raw_saml(request)
41+
@request = decode_raw_saml(request, settings)
4742
@document = REXML::Document.new(@request)
4843
end
4944

test/logoutresponse_test.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ class RubySamlTest < Minitest::Test
77

88
describe "Logoutresponse" do
99

10+
let(:default_settings) { OneLogin::RubySaml::Settings.new }
1011
let(:valid_logout_response_without_settings) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document) }
1112
let(:valid_logout_response) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings) }
1213

1314
describe "#new" do
1415
it "raise an exception when response is initialized with nil" do
1516
assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
1617
end
17-
it "default to empty settings" do
18-
assert_nil valid_logout_response_without_settings.settings
18+
it "default to default settings" do
19+
assert valid_logout_response_without_settings.settings.kind_of?(OneLogin::RubySaml::Settings)
1920
end
2021
it "accept constructor-injected settings" do
2122
refute_nil valid_logout_response.settings

test/saml_message_test.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class RubySamlTest < Minitest::Test
1010
let(:response_document_xml) { read_response("adfs_response_xmlns.xml") }
1111

1212
it "return decoded raw saml" do
13-
decoded_raw = saml_message.send(:decode_raw_saml, logout_request_deflated_base64)
13+
decoded_raw = saml_message.send(:decode_raw_saml, logout_request_deflated_base64, settings)
1414
assert logout_request_document, decoded_raw
1515
end
1616

@@ -64,9 +64,9 @@ class RubySamlTest < Minitest::Test
6464

6565
data = prefix + "A" * (200000 * 1024) + suffix
6666
bomb = Base64.encode64(Zlib::Deflate.deflate(data, 9)[2..-5])
67-
assert_raises(OneLogin::RubySaml::ValidationError, "Encoded SAML Message exceeds " + OneLogin::RubySaml::SamlMessage::MAX_BYTE_SIZE.to_s + " bytes, so was rejected") do
67+
assert_raises(OneLogin::RubySaml::ValidationError, "Encoded SAML Message exceeds " + OneLogin::RubySaml::Settings::DEFAULTS[:message_max_bytesize].to_s + " bytes, so was rejected") do
6868
saml_message = OneLogin::RubySaml::SamlMessage.new
69-
saml_message.send(:decode_raw_saml, bomb)
69+
saml_message.send(:decode_raw_saml, bomb, settings)
7070
end
7171
end
7272
end

test/settings_test.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class SettingsTest < Minitest::Test
1818
:single_logout_service_url, :single_logout_service_binding,
1919
:sp_name_qualifier, :name_identifier_format, :name_identifier_value, :name_identifier_value_requested,
2020
:sessionindex, :attributes_index, :passive, :force_authn,
21-
:compress_request, :double_quote_xml_attribute_values, :protocol_binding,
21+
:compress_request, :double_quote_xml_attribute_values, :message_max_bytesize, :protocol_binding,
2222
:security, :certificate, :private_key,
2323
:authn_context, :authn_context_comparison, :authn_context_decl_ref,
2424
:assertion_consumer_logout_service_url,
@@ -41,6 +41,7 @@ class SettingsTest < Minitest::Test
4141
:idp_sso_service_url => "http://sso.muda.no/sso",
4242
:idp_slo_service_url => "http://sso.muda.no/slo",
4343
:idp_cert_fingerprint => "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
44+
:message_max_bytesize => 750000,
4445
:valid_until => '2029-04-16T03:35:08.277Z',
4546
:name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
4647
:attributes_index => 30,

test/xml_security_test.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ class XmlSecurityTest < Minitest::Test
243243
settings.assertion_consumer_service_url = "https://sp.example.com/acs"
244244
settings.single_logout_service_url = "https://sp.example.com/sls"
245245
end
246+
let(:settings) { OneLogin::RubySaml::Settings.new }
246247

247248
it "sign an AuthNRequest" do
248249
request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)

0 commit comments

Comments
 (0)