Skip to content

Commit c21d693

Browse files
authored
Merge pull request #601 from gitlab-djensen/gitlab-djensen-use-setting-for-max-byte-size
Replace MAX_BYTE_SIZE constant with setting
2 parents ebe2317 + b6fa044 commit c21d693

File tree

8 files changed

+54
-19
lines changed

8 files changed

+54
-19
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,27 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :allowed_cloc
816816
817817
Make sure to keep the value as comfortably small as possible to keep security risks to a minimum.
818818
819+
## Deflation Limit
820+
821+
To protect against decompression bombs (a form of DoS attack), SAML messages are limited to 250,000 bytes by default.
822+
Sometimes legitimate SAML messages will exceed this limit,
823+
for example due to custom claims like including groups a user is a member of.
824+
If you want to customize this limit, you need to provide a different setting when initializing the response object.
825+
Example:
826+
827+
```ruby
828+
def consume
829+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], { settings: saml_settings })
830+
...
831+
end
832+
833+
private
834+
835+
def saml_settings
836+
OneLogin::RubySaml::Settings.new(message_max_bytesize: 500_000)
837+
end
838+
```
839+
819840
## Attribute Service
820841
821842
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def initialize(response, options = {})
6363
end
6464
end
6565

66-
@response = decode_raw_saml(response)
66+
@response = decode_raw_saml(response, settings)
6767
@document = XMLSecurity::SignedDocument.new(@response, @errors)
6868

6969
if assertion_encrypted?

lib/onelogin/ruby-saml/saml_message.rb

Lines changed: 4 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,12 @@ 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 = nil)
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+
settings = OneLogin::RubySaml::Settings.new if settings.nil?
93+
if saml.bytesize > settings.message_max_bytesize
94+
raise ValidationError.new("Encoded SAML Message exceeds " + settings.message_max_bytesize.to_s + " bytes, so was rejected")
9695
end
9796

9897
decoded = decode(saml)

lib/onelogin/ruby-saml/settings.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def initialize(overrides = {}, keep_security_attributes = false)
5454
attr_accessor :compress_request
5555
attr_accessor :compress_response
5656
attr_accessor :double_quote_xml_attribute_values
57+
attr_accessor :message_max_bytesize
5758
attr_accessor :passive
5859
attr_reader :protocol_binding
5960
attr_accessor :attributes_index
@@ -264,6 +265,7 @@ def get_binding(value)
264265
:idp_cert_fingerprint_algorithm => XMLSecurity::Document::SHA1,
265266
:compress_request => true,
266267
:compress_response => true,
268+
:message_max_bytesize => 250000,
267269
:soft => true,
268270
:double_quote_xml_attribute_values => false,
269271
:security => {

lib/onelogin/ruby-saml/slo_logoutrequest.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def initialize(request, options = {})
4343
end
4444
end
4545

46-
@request = decode_raw_saml(request)
46+
@request = decode_raw_saml(request, settings)
4747
@document = REXML::Document.new(@request)
4848
end
4949

test/saml_message_test.rb

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,33 @@ class RubySamlTest < Minitest::Test
5454
end
5555

5656
describe "Prevent Zlib bomb attack" do
57+
let(:bomb) { Base64.encode64(Zlib::Deflate.deflate(bomb_data, 9)[2..-5]) }
58+
let(:bomb_data) { bomb_prefix + "A" * (200000 * 1024) + bomb_suffix }
59+
let(:bomb_prefix) { """<?xml version='1.0' encoding='UTF-8'?>
60+
<samlp:LogoutRequest xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion' ID='ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d' Version='2.0' IssueInstant='2014-07-18T01:13:06Z' Destination='http://idp.example.com/SingleLogoutService.php'>
61+
<saml:Issuer>""" }
62+
let(:bomb_suffix) { """</saml:Issuer>
63+
<saml:NameID SPNameQualifier='http://sp.example.com/demo1/metadata.php' Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'>ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7</saml:NameID>
64+
</samlp:LogoutRequest>""" }
65+
5766
it "raises error when SAML Message exceed the allowed bytes" do
58-
prefix= """<?xml version='1.0' encoding='UTF-8'?>
59-
<samlp:LogoutRequest xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion' ID='ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d' Version='2.0' IssueInstant='2014-07-18T01:13:06Z' Destination='http://idp.example.com/SingleLogoutService.php'>
60-
<saml:Issuer>"""
61-
suffix= """</saml:Issuer>
62-
<saml:NameID SPNameQualifier='http://sp.example.com/demo1/metadata.php' Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'>ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7</saml:NameID>
63-
</samlp:LogoutRequest>"""
64-
65-
data = prefix + "A" * (200000 * 1024) + suffix
66-
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]} bytes, so was rejected") do
6868
saml_message = OneLogin::RubySaml::SamlMessage.new
6969
saml_message.send(:decode_raw_saml, bomb)
7070
end
7171
end
72+
73+
describe 'with a custom setting for message_max_bytesize' do
74+
let(:message_max_bytesize) { 100_00 }
75+
let(:settings) { OneLogin::RubySaml::Settings.new(message_max_bytesize: message_max_bytesize) }
76+
77+
it 'uses the custom setting' do
78+
assert_raises(OneLogin::RubySaml::ValidationError, "Encoded SAML Message exceeds #{message_max_bytesize} bytes, so was rejected") do
79+
saml_message = OneLogin::RubySaml::SamlMessage.new
80+
saml_message.send(:decode_raw_saml, bomb, settings)
81+
end
82+
end
83+
end
7284
end
7385
end
7486
end

test/settings_test.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class SettingsTest < Minitest::Test
1717
:idp_attribute_names, :issuer, :assertion_consumer_service_url, :single_logout_service_url,
1818
:sp_name_qualifier, :name_identifier_format, :name_identifier_value, :name_identifier_value_requested,
1919
:sessionindex, :attributes_index, :passive, :force_authn,
20-
:compress_request, :double_quote_xml_attribute_values,
20+
:compress_request, :double_quote_xml_attribute_values, :message_max_bytesize,
2121
:security, :certificate, :private_key,
2222
:authn_context, :authn_context_comparison, :authn_context_decl_ref,
2323
:assertion_consumer_logout_service_url
@@ -89,6 +89,7 @@ class SettingsTest < Minitest::Test
8989
:idp_slo_service_url => "http://sso.muda.no/slo",
9090
:idp_slo_service_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
9191
:idp_cert_fingerprint => "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
92+
:message_max_bytesize => 750000,
9293
:valid_until => '2029-04-16T03:35:08.277Z',
9394
:name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
9495
:attributes_index => 30,

0 commit comments

Comments
 (0)