Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Custom serializers and deserializers in MessageVerifier and MessageEn…

…cryptor.

By default, these classes use Marshal for serializing and deserializing messages. Unfortunately, the Marshal format is closely associated with Ruby internals and even changes between different interpreters. This makes the resulting message very hard to impossible to unserialize messages generated by these classes in other environments like node.js.

This patch solves this by allowing you to set your own custom serializer and deserializer lambda functions. By default, it still uses Marshal to be backwards compatible.
  • Loading branch information...
commit bffaa888ac4a1ee60a9f93650b9184a9402eff09 1 parent da7f042
Willem van Bergen authored
8 activesupport/lib/active_support/message_encryptor.rb
View
@@ -13,9 +13,13 @@ class MessageEncryptor
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
+ attr_accessor :serializer, :deserializer
+
def initialize(secret, cipher = 'aes-256-cbc')
@secret = secret
@cipher = cipher
+ @serializer = lambda { |value| Marshal.dump(value) }
+ @deserializer = lambda { |value| Marshal.load(value) }
end
def encrypt(value)
@@ -27,7 +31,7 @@ def encrypt(value)
cipher.key = @secret
cipher.iv = iv
- encrypted_data = cipher.update(Marshal.dump(value))
+ encrypted_data = cipher.update(serializer.call(value))
encrypted_data << cipher.final
[encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
@@ -44,7 +48,7 @@ def decrypt(encrypted_message)
decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final
- Marshal.load(decrypted_data)
+ deserializer.call(decrypted_data)
rescue OpenSSLCipherError, TypeError
raise InvalidMessage
end
8 activesupport/lib/active_support/message_verifier.rb
View
@@ -21,9 +21,13 @@ module ActiveSupport
class MessageVerifier
class InvalidSignature < StandardError; end
+ attr_accessor :serializer, :deserializer
+
def initialize(secret, digest = 'SHA1')
@secret = secret
@digest = digest
+ @serializer = lambda { |value| Marshal.dump(value) }
+ @deserializer = lambda { |value| Marshal.load(value) }
end
def verify(signed_message)
@@ -31,14 +35,14 @@ def verify(signed_message)
data, digest = signed_message.split("--")
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
- Marshal.load(ActiveSupport::Base64.decode64(data))
+ deserializer.call(ActiveSupport::Base64.decode64(data))
else
raise InvalidSignature
end
end
def generate(value)
- data = ActiveSupport::Base64.encode64s(Marshal.dump(value))
+ data = ActiveSupport::Base64.encode64s(serializer.call(value))
"#{data}--#{generate_digest(data)}"
end
10 activesupport/test/message_encryptor_test.rb
View
@@ -8,6 +8,7 @@
else
require 'active_support/time'
+require 'active_support/json'
class MessageEncryptorTest < Test::Unit::TestCase
def setup
@@ -38,7 +39,14 @@ def test_signed_round_tripping
message = @encryptor.encrypt_and_sign(@data)
assert_equal @data, @encryptor.decrypt_and_verify(message)
end
-
+
+ def test_alternative_serialization_method
+ @encryptor.serializer = lambda { |value| ActiveSupport::JSON.encode(value) }
+ @encryptor.deserializer = lambda { |value| ActiveSupport::JSON.decode(value) }
+
+ message = @encryptor.encrypt_and_sign({ :foo => 123, 'bar' => Time.local(2010) })
+ assert_equal @encryptor.decrypt_and_verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00-05:00" }
+ end
private
def assert_not_decrypted(value)
9 activesupport/test/message_verifier_test.rb
View
@@ -8,6 +8,7 @@
else
require 'active_support/time'
+require 'active_support/json'
class MessageVerifierTest < Test::Unit::TestCase
def setup
@@ -31,6 +32,14 @@ def test_tampered_data_raises
assert_not_verified("#{data}--#{hash.reverse}")
assert_not_verified("purejunk")
end
+
+ def test_alternative_serialization_method
+ @verifier.serializer = lambda { |value| ActiveSupport::JSON.encode(value) }
+ @verifier.deserializer = lambda { |value| ActiveSupport::JSON.decode(value) }
+
+ message = @verifier.generate({ :foo => 123, 'bar' => Time.local(2010) })
+ assert_equal @verifier.verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00-05:00" }
+ end
def assert_not_verified(message)
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
Please sign in to comment.
Something went wrong with that request. Please try again.