Rails: Accessing the decrypted attribute corrupts the encrypted attribute content
gr8bit opened this issue · comments
Environment
Ruby 2.5.1
Rails 5.2.1
Symmetric Encryption 4.1.1
Symmetric Encryption config for environment
development:
ciphers:
- key: 1234567890ABCDEF
iv: 1234567890ABCDEF
cipher_name: aes-128-cbc
version: 1
encoding: :none
always_add_header: true
class
class Foo < ActiveRecord::Base
attr_encrypted :sender, random_iv: true
end
create a new model
2.5.1 :001 > i=Foo.create! sender: "+123456"
=> #<Foo id: 19, created_at: "2018-10-27 19:16:08", updated_at: "2018-10-27 19:16:08", encrypted_sender: "@EnC\x01@\x10\x00\\\x9C\xA1H!$\x98\xD99Uk\x16!\x16\x84\xBA\\\x80\x8Av\"\xA5\x03\xB2]\xED~Q\xC6\x9Es\xB8">
2.5.1 :002 > i.encrypted_sender_change
=> nil
2.5.1 :003 > i.sender
=> "+123456"
2.5.1 :004 > i.sender.present?
=> true
2.5.1 :005 > i.encrypted_sender_change
=> nil
load the newly created model
2.5.1 :001 > i=Foo.last
=> #<Foo id: 19, created_at: "2018-10-27 19:16:08", updated_at: "2018-10-27 19:16:08", encrypted_sender: "@EnC\x01@\x10\x00\\\x9C\xA1H!$\x98\xD99Uk\x16!\x16\x84\xBA\\\x80\x8Av\"\xA5\x03\xB2]\xED~Q\xC6\x9Es\xB8">
2.5.1 :002 > i.encrypted_sender_change
=> nil
2.5.1 :003 > i.sender
=> "+123456"
2.5.1 :004 > i.encrypted_sender_change
=> ["@EnC\x01@\x10\x00\\\x9C\xA1H!$\x98\xD99Uk\x16!\x16\x84\xBA\\\x80\x8Av\"\xA5\x03\xB2]\xED~Q\xC6\x9Es\xB8", "\\\x80\x8Av\"\xA5\x03\xB2]\xED~Q\xC6\x9Es\xB8"]
Why does accessing the attribute trigger a change?
If I save the model, the encrypted_sender attribute is filled with corrupted data (actually saving fails thanks to the symmetric_encryption validator).
Expected Behavior
Accessing an encrypted attribute should not trigger a change, rendering the model in dirty state.
Actual Behavior
Accessing the "sender" encrypted attribute sets it with corrupted data (missing header?)
I found the bug. I use encoding: :none
in the config and the SymmetricEncryption::Encoder::None
is the only encoder which doesn't create a new string object on encode
and decode
but rather returns the passed string as-is.
At some point SymmetricEncryption.decrypt()
calls header.parse!(decoded)
which modifies the variable decoded
in place, which -in our case- is still the original encrypted string. So the original attribute becomes cropped in place and by that, invalid.
PR is on the way.