Instantly share code, notes, and snippets.
Created
January 31, 2012 22:06
-
Save stympy/1713342 to your computer and use it in GitHub Desktop.
Rails utility class for manually decoding session cookie values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# encoding: utf-8 | |
require 'base64' | |
# | |
# Utility class for rails applications: manually decode (session) cookies! | |
# | |
# Just drop this in you rails application's lib directory, run a rails console | |
# session, require the file, then create an instance in order to analyze your | |
# cookie header (either the "Set-Cookie" response or "Cookie" request header). | |
# | |
# Example: | |
# | |
# > require 'session_cookie_dump' | |
# > c = SessionCookieDump.new(File.read("./tmp/cookie.dump")) | |
# > puts c.cookie.inspect | |
# | |
# If the manual decoding of the cookie succeeded, you should be able to query | |
# the #decoded_cookie method to get the object instance that was serialized and | |
# stored as the value of the specified cookie. This object's value won't have | |
# been verified, just decoded. | |
# | |
# If the #cookie method returns an object, then the standard rails code was able | |
# to successfully decode AND VERIFY the object persisted in the cookie. | |
# | |
# If #decoded_cookie and #cookie are both set after the instance is constructed, | |
# their values should be two identical but seperate instances). | |
# | |
# All other attributes store intermediate- or meta-data and can be used to debug | |
# any decoding issues. | |
# | |
class SessionCookieDump | |
# | |
# Create a session-cookie dump of the provided cookie (a string containing the | |
# cookie as passed in the "Set-Cookie" response or "Cookie" request header. | |
# | |
def initialize(cookie) | |
# Retrieve rails app's settings | |
@errors = [] | |
@config_key = Rails.application.config.session_options[:key] | |
@config_secret = Rails.application.config.secret_token | |
# | |
# Initialize to defaults | |
# | |
@key = "<NOT-PRESENT>" | |
@raw = cookie | |
# | |
# Try to extract raw cookie data from input... (assumes SHA-1) | |
# | |
if @raw =~ /\A(.*?(([\w-]+)\s*=)?\s*)([A-Za-z0-9\/\+%]+(\s+[A-Za-z0-9\/\+%]+)*(\s*=(\s*=)?)?--[0-9A-Fa-f]{40})(;?.*)?\z/m | |
@key = $3 # session cookie entry's name | |
@raw_prefix = $1 # keep for posterity | |
@raw_cookie = $4 # actual session cookie entry's data | |
@raw_suffix = $8 # more "for posterity" data | |
if @key.blank? | |
@errors << "Session cookie's key ('#{@config_key}') not present" | |
else | |
@errors << "Session cookie's key is '#{@key}'; expected: '#{@config_key}'" if @key != @config_key | |
end | |
else | |
@errors << "Provided session cookie doesn't appear to be valid" | |
end | |
# | |
# Try to process raw cookie data... | |
# | |
unless @raw_cookie.blank? | |
@decoded_ws = @raw_cookie.split.join # nuke whitespace | |
end | |
unless @decoded_ws.blank? | |
@decoded_cgi = CGI.unescape(@decoded_ws) | |
end | |
unless @decoded_cgi.blank? | |
@decoded_parts = @decoded_cgi.split("--") | |
@decoded_hash = @decoded_parts.last | |
@decoded_base64 = Base64.decode64(@decoded_parts.first) | |
end | |
# | |
# Try to unmarshal cookie data... | |
# | |
@decoded_cookie = Marshal.load(@decoded_base64) unless @decoded_base64.blank? | |
# | |
# Now try to process cookie data using verifier... | |
# | |
unless @decoded_cgi.blank? | |
@verifier = ActiveSupport::MessageVerifier.new(@config_secret) | |
@cookie = @verifier.verify(@decoded_cgi) | |
end | |
end | |
attr_reader :key # name actually used for the cookie in the provided cookie data | |
attr_reader :config_key # name your rails app is configured to use for session cookie's name | |
attr_reader :config_secret # hold's your rails app's secret key | |
attr_reader :errors # an array of error strings from processing the cookie | |
attr_reader :verifier # the ActiveSupport::MessageVerifier instance used to verify cookie | |
attr_reader :raw # the raw cookie data as passed into #new | |
attr_reader :raw_cookie # the raw cookie data w/leading data, cookie name, and trailing data stripped off | |
attr_reader :raw_prefix # any leading data that was stripped off of the raw cookie | |
attr_reader :raw_suffix # any trailing data that was stripped off of the raw cookie | |
attr_reader :decoded_ws # value of #raw_cookie after all whitespace was removed | |
attr_reader :decoded_cgi # value of #decoded_ws after it was CGI unescaped | |
attr_reader :decoded_parts # value of #decoded_cgi split into 2-element array at the "--" (double dash) | |
attr_reader :decoded_hash # value of #decoded_parts[1]; this is SHA-1 hash of the cookie data (#decoded_parts[0]) | |
attr_reader :decoded_base64 # value of #decoded_parts[0] after base-64 encoding removed | |
attr_reader :decoded_cookie # value of #decoded_base64 after converting it to an object (using Marshal.load) | |
attr_reader :cookie # value of object (should be same as #decoded_cookie) after being processed using #verifier | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment