1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #24510 from vipulnsward/make-variable_size_secure_compare-public

Make variable_size_secure_compare public
This commit is contained in:
Rafael Mendonça França 2017-11-25 11:39:37 -05:00
commit 0623b5d194
No known key found for this signature in database
GPG key ID: FC23B6D0F1EEE948
5 changed files with 37 additions and 19 deletions

View file

@ -72,10 +72,10 @@ module ActionController
before_action(options.except(:name, :password, :realm)) do before_action(options.except(:name, :password, :realm)) do
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password| authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
# This comparison uses & so that it doesn't short circuit and # This comparison uses & so that it doesn't short circuit and
# uses `variable_size_secure_compare` so that length information # uses `secure_compare` so that length information
# isn't leaked. # isn't leaked.
ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) & ActiveSupport::SecurityUtils.secure_compare(name, options[:name]) &
ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password]) ActiveSupport::SecurityUtils.secure_compare(password, options[:password])
end end
end end
end end
@ -350,10 +350,7 @@ module ActionController
# authenticate_or_request_with_http_token do |token, options| # authenticate_or_request_with_http_token do |token, options|
# # Compare the tokens in a time-constant manner, to mitigate # # Compare the tokens in a time-constant manner, to mitigate
# # timing attacks. # # timing attacks.
# ActiveSupport::SecurityUtils.secure_compare( # ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
# ::Digest::SHA256.hexdigest(token),
# ::Digest::SHA256.hexdigest(TOKEN)
# )
# end # end
# end # end
# end # end

View file

@ -369,7 +369,7 @@ module ActionController #:nodoc:
end end
def compare_with_real_token(token, session) # :doc: def compare_with_real_token(token, session) # :doc:
ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session)) ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, real_csrf_token(session))
end end
def valid_per_form_csrf_token?(token, session) # :doc: def valid_per_form_csrf_token?(token, session) # :doc:
@ -380,7 +380,7 @@ module ActionController #:nodoc:
request.request_method request.request_method
) )
ActiveSupport::SecurityUtils.secure_compare(token, correct_token) ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, correct_token)
else else
false false
end end

View file

@ -1,3 +1,11 @@
* Changed default behaviour of `ActiveSupport::SecurityUtils.secure_compare`,
to make it not leak length information even for variable length string.
Renamed old `ActiveSupport::SecurityUtils.secure_compare` to `fixed_length_secure_compare`,
and started raising `ArgumentError` in case of length mismatch of passed strings.
*Vipul A M*
* Make `ActiveSupport::TimeZone.all` return only time zones that are in * Make `ActiveSupport::TimeZone.all` return only time zones that are in
`ActiveSupport::TimeZone::MAPPING`. `ActiveSupport::TimeZone::MAPPING`.

View file

@ -4,14 +4,12 @@ require "digest/sha2"
module ActiveSupport module ActiveSupport
module SecurityUtils module SecurityUtils
# Constant time string comparison. # Constant time string comparison, for fixed length strings.
# #
# The values compared should be of fixed length, such as strings # The values compared should be of fixed length, such as strings
# that have already been processed by HMAC. This should not be used # that have already been processed by HMAC. Raises in case of length mismatch.
# on variable length plaintext strings because it could leak length info def fixed_length_secure_compare(a, b)
# via timing attacks. raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
def secure_compare(a, b)
return false unless a.bytesize == b.bytesize
l = a.unpack "C#{a.bytesize}" l = a.unpack "C#{a.bytesize}"
@ -19,11 +17,15 @@ module ActiveSupport
b.each_byte { |byte| res |= byte ^ l.shift } b.each_byte { |byte| res |= byte ^ l.shift }
res == 0 res == 0
end end
module_function :secure_compare module_function :fixed_length_secure_compare
def variable_size_secure_compare(a, b) # :nodoc: # Constant time string comparison, for variable length strings.
secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) #
# The values are first processed by SHA256, so that we don't leak length info
# via timing attacks.
def secure_compare(a, b)
fixed_length_secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b))
end end
module_function :variable_size_secure_compare module_function :secure_compare
end end
end end

View file

@ -13,4 +13,15 @@ class SecurityUtilsTest < ActiveSupport::TestCase
assert ActiveSupport::SecurityUtils.variable_size_secure_compare("a", "a") assert ActiveSupport::SecurityUtils.variable_size_secure_compare("a", "a")
assert_not ActiveSupport::SecurityUtils.variable_size_secure_compare("a", "b") assert_not ActiveSupport::SecurityUtils.variable_size_secure_compare("a", "b")
end end
def test_fixed_length_secure_compare_should_perform_string_comparison
assert ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "a")
assert !ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "b")
end
def test_fixed_length_secure_compare_raise_on_length_mismatch
assert_raises(ArgumentError, "string length mismatch.") do
ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "ab")
end
end
end end