Skip to content

Instantly share code, notes, and snippets.

@jeffrafter
Forked from mattetti/rails_json_session.rb
Last active December 31, 2015 06:59
Show Gist options
  • Save jeffrafter/7950832 to your computer and use it in GitHub Desktop.
Save jeffrafter/7950832 to your computer and use it in GitHub Desktop.
# https://gist.github.com/mattetti/7624413
#
# Hack to change the Rails cookie serializer from Marshal to JSON and therefore allow the session
# to be shared between different languages but also avoid that someone knowing the
# cookie secret key could execute arbitrary code on the server by unmarshalling
# modified Ruby code added to the session/permanent cookie.
#
# Note that all users will need to login again since both the remember me cookie and the session cookies
# won't be valid. Note also that the remember me cookie is tested multiple times per request even when it fails.
# for performance reasons you might want to delete it if these extra cycles are too costly for you.
#
# Rails 4 (not tested on Rails 3).
# Author: Matt Aimonetti / mattetti
# Rails issue (more info, discussion, updates): https://github.com/rails/rails/issues/12881
# Wrapper module around `JSON` so we can rescue a parsing error
module JsonSessionSerializer
def self.load(value)
begin
# Allow for non JSON compliant values (e.g., single strings). Note,
# do not use the built-in JSON.load to avoid dangerous side-effects
JSON.parse(value, create_additions: false, quirks_mode: true)
rescue JSON::ParserError
nil
end
end
def self.dump(value)
# Allow for non JSON compliant values (e.g., single strings)
JSON.generate(value, quirks_mode: true)
end
end
# Make cookies use JSON as serializer instead of Marshal (Rails 4)
module ActionDispatch
class Cookies
# used by the remember me and other permanent cookies.
class SignedCookieJar #:nodoc:
include ChainedCookieJars
def initialize(parent_jar, key_generator, options = {})
@parent_jar = parent_jar
@options = options
secret = key_generator.generate_key(@options[:signed_cookie_salt])
# The only actual change is to pass a serializer options with a default set to our JSON serializer.
@verifier = ActiveSupport::MessageVerifier.new(secret, { serializer: options[:serializer] || JsonSessionSerializer })
end
end
# used by the session cookie.
class EncryptedCookieJar #:nodoc:
include ChainedCookieJars
def initialize(parent_jar, key_generator, options = {})
if ActiveSupport::LegacyKeyGenerator === key_generator
raise "You didn't set config.secret_key_base, which is required for this cookie jar. " +
"Read the upgrade documentation to learn more about this new config option."
end
@parent_jar = parent_jar
@options = options
secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
# The only actual change is to pass a serializer options with a default set to our JSON serializer.
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, { serializer: options[:serializer] || JsonSessionSerializer } )
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment