app/controllers/ahoy/messages_controller.rb in ahoy_email-0.1.0 vs app/controllers/ahoy/messages_controller.rb in ahoy_email-0.1.1

- old
+ new

@@ -14,21 +14,33 @@ if @message and !@message.clicked_at @message.clicked_at = Time.now @message.save! end url = params[:url] - signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new("sha1"), AhoyEmail.secret_token, url) - if params[:signature] == signature + signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha1"), AhoyEmail.secret_token, url) + if secure_compare(params[:signature], signature) redirect_to url else redirect_to main_app.root_url end end protected def set_message @message = AhoyEmail.message_model.where(token: params[:id]).first + end + + # from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/message_verifier.rb + # constant-time comparison algorithm to prevent timing attacks + def secure_compare(a, b) + return false unless a.bytesize == b.bytesize + + l = a.unpack "C#{a.bytesize}" + + res = 0 + b.each_byte { |byte| res |= byte ^ l.shift } + res == 0 end end end