Skip to content

Commit 07bc2bc

Browse files
authored
Improve the inflate method to prevent potential DoS vulnerability in Zlib::Inflate (#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.
1 parent 0d787e8 commit 07bc2bc

File tree

1 file changed

+46
-13
lines changed

1 file changed

+46
-13
lines changed

lib/onelogin/ruby-saml/saml_message.rb

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class SamlMessage
1818
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion".freeze
1919
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol".freeze
2020

21-
BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z)
21+
BASE64_FORMAT = %r{\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z}
2222

2323
# @return [Nokogiri::XML::Schema] Gets the schema object of the SAML 2.0 Protocol schema
2424
#
@@ -91,18 +91,13 @@ def decode_raw_saml(saml, settings = nil)
9191

9292
return saml unless base64_encoded?(saml)
9393

94-
decoded = decode(saml)
95-
begin
96-
message = inflate(decoded)
97-
rescue
98-
message = decoded
99-
end
94+
saml = try_inflate(decode(saml), settings.message_max_bytesize)
10095

101-
if message.bytesize > settings.message_max_bytesize
96+
if saml.bytesize > settings.message_max_bytesize
10297
raise ValidationError.new("SAML Message exceeds " + settings.message_max_bytesize.to_s + " bytes, so was rejected")
10398
end
10499

105-
message
100+
saml
106101
end
107102

108103
# Deflate, base64 encode and url-encode a SAML Message (To be used in the HTTP-redirect binding)
@@ -144,12 +139,50 @@ def base64_encoded?(string)
144139
!!string.gsub(/[\r\n]|\\r|\\n|\s/, "").match(BASE64_FORMAT)
145140
end
146141

147-
# Inflate method
142+
# Attempt inflating a string, if it fails, return the original string.
143+
# @param data [String] The string
144+
# @param max_bytesize [Integer] The maximum allowed size of the SAML Message,
145+
# to prevent a possible DoS attack.
146+
# @return [String] The inflated or original string
147+
def try_inflate(data, max_bytesize = nil)
148+
inflate(data, max_bytesize)
149+
rescue Zlib::Error
150+
data
151+
end
152+
153+
# Inflate method.
148154
# @param deflated [String] The string
155+
# @param max_bytesize [Integer] The maximum allowed size of the SAML Message,
156+
# to prevent a possible DoS attack.
149157
# @return [String] The inflated string
150-
#
151-
def inflate(deflated)
152-
Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(deflated)
158+
def inflate(deflated, max_bytesize = nil)
159+
unless max_bytesize.nil?
160+
inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
161+
162+
# Use a StringIO buffer to build the inflated message incrementally.
163+
buffer = StringIO.new
164+
165+
inflater.inflate(deflated) do |chunk|
166+
if buffer.length + chunk.bytesize > max_bytesize
167+
inflater.close
168+
raise ValidationError, "SAML Message exceeds #{max_bytesize} bytes during decompression, so was rejected"
169+
end
170+
buffer << chunk
171+
end
172+
173+
final_chunk = inflater.finish
174+
unless final_chunk.empty?
175+
if buffer.length + final_chunk.bytesize > max_bytesize
176+
raise ValidationError, "SAML Message exceeds #{max_bytesize} bytes during decompression, so was rejected"
177+
end
178+
buffer << final_chunk
179+
end
180+
181+
inflater.close
182+
buffer.string
183+
else
184+
Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(deflated)
185+
end
153186
end
154187

155188
# Deflate method

0 commit comments

Comments
 (0)