The project is in a healthy, maintained state
Encrypted attributes for ROM
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 3.0
>= 1.0

Runtime

~> 1.5
 Project Readme

RomEncryptedAttribute

This gem adds support for encrypted attributes to ROM.

Traditionally ROM team suggested to put encryption logic in repository code (more precisely, in the mapper from database struct to an entity). I personally think this is not the greatest idea. Repository lies logically in the application layer (or even domain layer), while encryption and decryption of data is a purely infrastructure concern. As such, it should be as low-level and hidden as possible.

In ROM terms it means doing it in a relation. The gem leverages custom types to achieve encryption and decryption.

The scheme is compatible with Rails' default settings for ActiveRecord encryption, so you can still read records encrypted with ActiveRecord from ROM (and vice versa) as long as you provide the same primary key and key derivation salt.

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add rom_encrypted_attribute

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install rom_encrypted_attribute

Usage

In your relation, define custom types using a helper method from the gem. You need to provide the credentials to it somehow. This might be done via environmental variables, Hanami settings (if you're using Hanami) or any other means, really.

class SecretNotes < ROM::Relation[:sql]
  EncryptedString, EncryptedStringReader =
    RomEncryptedAttribute.define_encrypted_attribute_types(
      primary_key: ENV["ENCRYPTION_PRIMARY_KEY"],
      key_derivation_salt: ENV["ENCRYPTION_KEY_DERIVATION_SALT"]
    )
    
  schema(:secret_notes, infer: true) do
    attribute :content, EncryptedString, read: EncryptedStringReader
  end
end

By default the gem uses SHA1 for key derivation (same as Rails' default), but you can configure it by passing custom has_digest_class option.

class SecretNotes < ROM::Relation[:sql]
  EncryptedString, EncryptedStringReader =
    RomEncryptedAttribute.define_encrypted_attribute_types(
      primary_key: ENV["ENCRYPTION_PRIMARY_KEY"],
      key_derivation_salt: ENV["ENCRYPTION_KEY_DERIVATION_SALT"],
      hash_digest_class: OpenSSL::Digest::SHA256
    )
    
  schema(:secret_notes, infer: true) do
    attribute :content, EncryptedString, read: EncryptedStringReader
  end
end

Caveats

  • Due to a bug in rom-sql, reading unencrypted data is always supported, which means that if there's a plain not-encrypted data in your database already, it will be read correctly. This might or might not be desirable, but for the time being there's no choice in cofiguring this behaviour.
  • Support for deterministic encryption from ActiveRecord::Encryption is not implemented
  • Support for key rotation is not implemented

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/katafrakt/rom_encrypted_attribute.