CVE-2018-1000119: Observable Discrepancy (CWE-203)
value: Sinatra rack-protection versions 1.5.4 and 2.0.0.rc3 and earlier contains a timing attack vulnerability in the CSRF token checking that can result in signatures can be exposed. This attack appear to be exploitable via network connectivity to the ruby application. This vulnerability appears to have been fixed in 1.5.5 and 2.0.0.
CWE description: The product behaves differently or sends different responses under different circumstances in a way that is observable to an unauthorized actor, which exposes security-relevant information about the state of the product, such as whether a particular operation was successful or not.
CVSS v3 base score: 5.9
Published: 2018-03-07T14:29Z
Repository: https://github.com/sinatra/sinatra
Language: Ruby
Files changed: rack-protection/lib/rack/protection/authenticity_token.rb, rack-protection/lib/rack/protection/base.rb
Commit date: 2016-07-26 15:31:31 +0900
Commit message: Use secure_compare when checking CSRF token

Since string comparisions may return early we want to use a constant
time comparsion function to protect the CSRF token against timing
attacks. Rack::Utils provides a such function.

Diff: diff --git a/rack-protection/lib/rack/protection/authenticity_token.rb b/rack-protection/lib/rack/protection/authenticity_token.rb
index e4524e4..ad2ab9c 100644
--- a/rack-protection/lib/rack/protection/authenticity_token.rb
+++ b/rack-protection/lib/rack/protection/authenticity_token.rb
@@ -21,11 +21,11 @@ module Rack
 
       def accepts?(env)
         session = session env
         token   = session[:csrf] ||= session['_csrf_token'] || random_string
         safe?(env) ||
-          env['HTTP_X_CSRF_TOKEN'] == token ||
-          Request.new(env).params[options[:authenticity_param]] == token
+          secure_compare(env['HTTP_X_CSRF_TOKEN'], token) ||
+          secure_compare(Request.new(env).params[options[:authenticity_param]], token)
       end
     end
   end
 end
diff --git a/rack-protection/lib/rack/protection/base.rb b/rack-protection/lib/rack/protection/base.rb
index fe6ab37..c914da2 100644
--- a/rack-protection/lib/rack/protection/base.rb
+++ b/rack-protection/lib/rack/protection/base.rb
@@ -1,6 +1,7 @@
 require 'rack/protection'
+require 'rack/utils'
 require 'digest'
 require 'logger'
 require 'uri'
 
 module Rack
@@ -108,10 +109,14 @@ module Rack
 
       def encrypt(value)
         options[:encryptor].hexdigest value.to_s
       end
 
+      def secure_compare(a, b)
+        Rack::Utils.secure_compare(a.to_s, b.to_s)
+      end
+
       alias default_reaction deny
 
       def html?(headers)
         return false unless header = headers.detect { |k,v| k.downcase == 'content-type' }
         options[:html_types].include? header.last[/^\w+\/\w+/]

Vulnerable code:           env['HTTP_X_CSRF_TOKEN'] == token ||
          Request.new(env).params[options[:authenticity_param]] == token
Fixed code:           secure_compare(env['HTTP_X_CSRF_TOKEN'], token) ||
          secure_compare(Request.new(env).params[options[:authenticity_param]], token)
require 'rack/utils'
      def secure_compare(a, b)
        Rack::Utils.secure_compare(a.to_s, b.to_s)
      end