public
Last active

This is a monkey patch to change Rails 4's default session/signed cookie serializer from Marshal to JSON for security and compatibility reasons. Note that this is a hack, a pretty terrible one and you should only use it if you know what you're doing. Also, I only wrote this patch for my own personal use, so don't be surprised if it doesn't work for you. Usage: require this file in an initializer. (might work for Rails 3)

  • Download Gist
rails_json_session.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
# 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 beed 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
JSON.parse(value)
rescue JSON::ParserError
nil
end
end
 
def self.dump(value)
JSON.generate(value)
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

Would something like this make sense as a PR to rails? Perhaps default to the current marshal behavior but allow override to a custom serializer class via configuration?

Matteti wrote on his blog http://matt.aimonetti.net/posts/2013/11/30/sharing-rails-sessions-with-non-ruby-apps/:

Rails doesn’t let you change the default serializer directly. But Rails relies on ActiveSupport for its crypto work and AS supports swapping the serializer. Some people in the community are aware of this issue and monkey patch Rails to serialize their sessions using JSON or another alternative. Here is an Airbnb article and Rails 3 patch. Here is my Rails 4 monkey patch to switch the serialization to JSON.

His post can help you figure out.

There's a discussion started by @mattetti about the issue here: https://github.com/rails/rails/issues/12881

Thanks Matt!

Note: if you want to allow for values that are not hashes or arrays (i.e., single strings) you need to enable quirks_mode when generating and parsing. See https://gist.github.com/jeffrafter/7950832

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.