VYPR
Moderate severityNVD Advisory· Published Sep 8, 2009· Updated Apr 23, 2026

CVE-2009-3086

CVE-2009-3086

Description

A certain algorithm in Ruby on Rails 2.1.0 through 2.2.2, and 2.3.x before 2.3.4, leaks information about the complexity of message-digest signature verification in the cookie store, which might allow remote attackers to forge a digest via multiple attempts.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
actionpackRubyGems
>= 2.1.0, < 2.2.32.2.3
actionpackRubyGems
>= 2.3.0, < 2.3.42.3.4
activesupportRubyGems
>= 2.1.0, < 2.2.32.2.3
activesupportRubyGems
>= 2.3.0, < 2.3.42.3.4

Affected products

8
  • Rubyonrails/Rails8 versions
    cpe:2.3:a:rubyonrails:rails:2.1.0:*:*:*:*:*:*:*+ 7 more
    • cpe:2.3:a:rubyonrails:rails:2.1.0:*:*:*:*:*:*:*
    • cpe:2.3:a:rubyonrails:rails:2.1.1:*:*:*:*:*:*:*
    • cpe:2.3:a:rubyonrails:rails:2.1.2:*:*:*:*:*:*:*
    • cpe:2.3:a:rubyonrails:rails:2.2.0:*:*:*:*:*:*:*
    • cpe:2.3:a:rubyonrails:rails:2.2.1:*:*:*:*:*:*:*
    • cpe:2.3:a:rubyonrails:rails:2.2.2:*:*:*:*:*:*:*
    • cpe:2.3:a:rubyonrails:rails:2.3.2:*:*:*:*:*:*:*
    • cpe:2.3:a:rubyonrails:rails:2.3.3:*:*:*:*:*:*:*

Patches

3
674f780d59a5

Fix timing attack vulnerability in the Cookie Store

https://github.com/rails/railsMichael KoziarskiAug 23, 2009via ghsa
1 file changed · +14 1
  • actionpack/lib/action_controller/session/cookie_store.rb+14 1 modified
    @@ -140,7 +140,7 @@ def unmarshal(cookie)
             data, digest = cookie.split('--')
     
             # Do two checks to transparently support old double-escaped data.
    -        unless digest == generate_digest(data) || digest == generate_digest(data = CGI.unescape(data))
    +        unless secure_compare(digest, generate_digest(data)) || secure_compare(digest, generate_digest(data = CGI.unescape(data)))
               delete
               raise TamperedWithCookie
             end
    @@ -164,4 +164,17 @@ def write_cookie(options)
         def clear_old_cookie_value
           @session.cgi.cookies[@cookie_options['name']].clear
         end
    +    
    +    # constant-time comparison algorithm to prevent timing attacks
    +    def secure_compare(a, b)
    +      if a.length == b.length
    +        result = 0
    +        for i in 0..(a.length - 1)
    +          result |= a[i] ^ b[i]
    +        end
    +        result == 0
    +      else
    +        false
    +      end
    +    end
     end
    
1f07a89c5946

Fix timing attack vulnerability in ActiveSupport::MessageVerifier.

https://github.com/rails/railsCoda HaleAug 13, 2009via ghsa
1 file changed · +16 3
  • activesupport/lib/active_support/message_verifier.rb+16 3 modified
    @@ -25,10 +25,10 @@ def initialize(secret, digest = 'SHA1')
         
         def verify(signed_message)
           data, digest = signed_message.split("--")
    -      if digest != generate_digest(data)
    -        raise InvalidSignature
    -      else
    +      if secure_compare(digest, generate_digest(data))
             Marshal.load(ActiveSupport::Base64.decode64(data))
    +      else
    +        raise InvalidSignature
           end
         end
         
    @@ -38,6 +38,19 @@ def generate(value)
         end
         
         private
    +      # constant-time comparison algorithm to prevent timing attacks
    +      def secure_compare(a, b)
    +        if a.length == b.length
    +          result = 0
    +          for i in 0..(a.length - 1)
    +            result |= a[i] ^ b[i]
    +          end
    +          result == 0
    +        else
    +          false
    +        end
    +      end
    +
           def generate_digest(data)
             require 'openssl' unless defined?(OpenSSL)
             OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data)
    
d460c9a25560

Add ActiveSupport::MessageVerifier to aid users who need to store tamper-proof messages in cookies etc.

https://github.com/rails/railsMichael KoziarskiNov 23, 2008via ghsa
4 files changed · +73 0
  • activesupport/CHANGELOG+2 0 modified
    @@ -1,5 +1,7 @@
     *2.3.0 [Edge]*
     
    +* Added ActiveSupport::MessageVerifier to aid users who need to store signed messages. [Koz]
    +
     * Added ActiveSupport::BacktraceCleaner to cut down on backtrace noise according to filters and silencers [DHH]
     
     * Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) [Chris Wanstrath]
    
  • activesupport/lib/active_support/message_verifier.rb+45 0 added
    @@ -0,0 +1,45 @@
    +module ActiveSupport
    +  # MessageVerifier makes it easy to generate and verify messages which are signed
    +  # to prevent tampering.
    +  # 
    +  # This is useful for cases like remember-me tokens and auto-unsubscribe links where the
    +  # session store isn't suitable or available.
    +  #
    +  # Remember Me:
    +  #   cookies[:remember_me] = @verifier.generate_message([@user.id, 2.weeks.from_now])
    +  # 
    +  # In the authentication filter:
    +  #
    +  #   id, time = @verifier.verify_message(cookies[:remember_me])
    +  #   if time < Time.now
    +  #     self.current_user = User.find(id)
    +  #   end
    +  # 
    +  class MessageVerifier
    +    class InvalidSignature < StandardError; end
    +    
    +    def initialize(secret, digest = 'SHA1')
    +      @secret = secret
    +      @digest = digest
    +    end
    +    
    +    def verify_message(signed_message)
    +      data, digest = signed_message.split("--")
    +      if digest != generate_digest(data)
    +        raise InvalidSignature
    +      else
    +        Marshal.load(ActiveSupport::Base64.decode64(data))
    +      end
    +    end
    +    
    +    def generate_message(value)
    +      data = ActiveSupport::Base64.encode64s(Marshal.dump(value))
    +      "#{data}--#{generate_digest(data)}"
    +    end
    +    
    +    private
    +      def generate_digest(data)
    +        OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data)
    +      end
    +  end
    +end
    \ No newline at end of file
    
  • activesupport/lib/active_support.rb+1 0 modified
    @@ -56,6 +56,7 @@
     require 'active_support/time_with_zone'
     
     require 'active_support/secure_random'
    +require 'active_support/message_verifier'
     
     require 'active_support/rescuable'
     
    
  • activesupport/test/message_verifier_test.rb+25 0 added
    @@ -0,0 +1,25 @@
    +require 'abstract_unit'
    +
    +class MessageVerifierTest < Test::Unit::TestCase
    +  def setup
    +    @verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
    +    @data = {:some=>"data", :now=>Time.now}
    +  end
    +  
    +  def test_simple_round_tripping
    +    message = @verifier.generate_message(@data)
    +    assert_equal @data, @verifier.verify_message(message)
    +  end
    +  
    +  def test_tampered_data_raises
    +    data, hash = @verifier.generate_message(@data).split("--")
    +    assert_not_verified("#{data.reverse}--#{hash}")
    +    assert_not_verified("#{data}--#{hash.reverse}")
    +  end
    +  
    +  def assert_not_verified(message)
    +    assert_raises(ActiveSupport::MessageVerifier::InvalidSignature) do
    +      @verifier.verify_message(message)
    +    end
    +  end
    +end
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

16

News mentions

0

No linked articles in our index yet.