Skip to content

Instantly share code, notes, and snippets.

@rboyd
Created October 15, 2010 10:00
Show Gist options
  • Select an option

  • Save rboyd/627939 to your computer and use it in GitHub Desktop.

Select an option

Save rboyd/627939 to your computer and use it in GitHub Desktop.
# Merb SessionCookie unmarshalling with validation against secret key. Useful for 3rd service
# (like socket server) to map cookie -> user_id. Pretty much all of this jacked from Merb
# source and distilled into the same place.
require 'base64' # to convert Marshal.dump to ASCII
require 'openssl' # to generate the HMAC message digest
# Cookies can typically store 4096 bytes.
MAX = 4096
DIGEST = OpenSSL::Digest::Digest.new('SHA1') # or MD5, RIPEMD160, SHA256?
SECRET = 'REDACTED'
# Unescapes a URI escaped string. (Stolen from Camping).
def unescape(s)
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
[$1.delete('%')].pack('H*')
}
end
def unserialize(data)
Marshal.load(Base64.decode64(data)) rescue {}
end
# ==== Returns
# Boolean:: Do the digests validate?
def secure_compare(a, b)
if a.length == b.length
# unpack to forty characters.
# needed for 1.8 and 1.9 compat
a_bytes = a.unpack('C*')
b_bytes = b.unpack('C*')
result = 0
for i in 0..(a_bytes.length - 1)
result |= a_bytes[i] ^ b_bytes[i]
end
result == 0
else
false
end
end
# Generate the HMAC keyed message digest. Uses SHA1.
#
# ==== Returns
# String:: an HMAC digest of the cookie data.
#
# :api: private
def generate_digest(data)
OpenSSL::HMAC.hexdigest(DIGEST, SECRET, data)
end
def blank?
nil? || (respond_to?(:empty?) && empty?)
end
def unmarshal(cookie)
if cookie.blank?
{}
else
data, digest = unescape(cookie).split('--')
return {} if data.blank? || digest.blank?
unless secure_compare(generate_digest(data), digest)
return "Maybe the site's session_secret_key has changed?"
end
unserialize(data)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment