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([@user.id, 2.weeks.from_now])
In the authentication filter:
id, time = @verifier.verify(cookies[:remember_me]) if time < Time.now self.current_user = User.find(id) end
By default it uses Marshal to serialize the message. If you want to use another serialization method, you can set the serializer attribute to something that responds to dump and load, e.g.:
@verifier.serializer = YAML
# File lib/active_support/message_verifier.rb, line 30 30: def initialize(secret, options = {}) 31: unless options.is_a?(Hash) 32: ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :digest => 'algorithm' to specify the digest algorithm." 33: options = { :digest => options } 34: end 35: 36: @secret = secret 37: @digest = options[:digest] || 'SHA1' 38: @serializer = options[:serializer] || Marshal 39: end
# File lib/active_support/message_verifier.rb, line 52 52: def generate(value) 53: data = ::Base64.strict_encode64(@serializer.dump(value)) 54: "#{data}--#{generate_digest(data)}" 55: end
# File lib/active_support/message_verifier.rb, line 41 41: def verify(signed_message) 42: raise InvalidSignature if signed_message.blank? 43: 44: data, digest = signed_message.split("--") 45: if data.present? && digest.present? && secure_compare(digest, generate_digest(data)) 46: @serializer.load(::Base64.decode64(data)) 47: else 48: raise InvalidSignature 49: end 50: end
# File lib/active_support/message_verifier.rb, line 69 69: def generate_digest(data) 70: require 'openssl' unless defined?(OpenSSL) 71: OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data) 72: end
constant-time comparison algorithm to prevent timing attacks
# File lib/active_support/message_verifier.rb, line 59 59: def secure_compare(a, b) 60: return false unless a.bytesize == b.bytesize 61: 62: l = a.unpack "C#{a.bytesize}" 63: 64: res = 0 65: b.each_byte { |byte| res |= byte ^ l.shift } 66: res == 0 67: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.