If you're hosting your web server in a particular region, it might be necessary to comply with the GDPR norms of that region. Anonymizing and encrypting data becomes necessary in such situations. In this blog, we will discuss the attribute encryption that Rails 7 provides right out of the box. And we will also see the Deterministic & Non Deterministic approaches. (If you're using Rails version lesser than 7, check out our previous blog here on how to write a custom encryption framework.)
Apart from this, encryption also helps protect against data breach. Sensitive information like customer's email, phone or home address must be encrypted and filtered at the application level as it adds an additional layer of protection against unauthorized access to the database, application console or server logs.
Installing Rails 7
As of the time of writing this blog, Rails 7 is still in the alpha phase. You can install it by adding this to your Gemfile.
gem 'rails', github: 'rails/rails', branch: 'main'
bundle install to install the gem and verify the gem version using the
rails -v command.
$ rails -v Rails 7.0.0.alpha
Voila! You've successfully installed Rails 7.
Initializing Encryption keys
To get started with encryption, first, we'd need to initialize some keys that will be used by the
encrypts API to encrypt and decrypt attributes.
bin/rails db:encryption:init to generate a random key set.
$ bin/rails db:encryption:init Add this entry to the credentials of the target environment: active_record_encryption: primary_key: EGY8WhulUOXixybod7ZWwMIL68R9o5kC deterministic_key: aPA5XyALhf75NNnMzaspW7akTfZp0lPY key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz
Add the generated key set to your RoR application's credentials file.
primary_key- Used to derive the root encryption key for non-deterministic encryption.
deterministic_key- Used to derive the root encryption key for deterministic encryption.
key_derivation_salt- Sequence of bits added to the root key to strengthen it further.
Declaration of Encrypted Attribute
To encrypt any attribute, we simply need to add a declaration
encrypts at the model level.
class User encrypts :email end
Deterministic & Non Deterministic approach
By default, Rails uses a non-deterministic (also known as probabilistic) approach to encrypt the plain text. Under the hood, it uses a random initialization vector to encrypt the plain text. Due to this, the same plain text will generate a different cipher each time it is encrypted making it harder for the attacker to find patterns in the encrypted data and eventually guessing the key.
But wait a minute. Querying non-deterministically encrypted data is impossible now.
User.create email: 'email@example.com' # => <User:0x00 id: 1, email: "firstname.lastname@example.org"...> User.find_by email: 'email@example.com' # => nil
If you want to directly query an encrypted column attribute, you'd need to use the deterministic approach. For this, simply use the
deterministic: true option during declaration.
class User encrypts :email, deterministic: true end User.create email: 'firstname.lastname@example.org' # => <User:0x00 id: 1, email: "email@example.com"...> User.find_by email: 'firstname.lastname@example.org' # => <User:0x00 id: 1, email: "email@example.com"...>
Under the hood, rails will use a fixed initialization vector for encryption. As you might've already guessed, this is not very secure. Hence, it is recommended to use non-deterministic approach if querying the column is not required.
Encryption Key Management
Encryption Key Rotation
ActiveRecord Encryption allows support for key rotation by providing a list of keys to
active_record: encryption: primary_key: - a1cc4d7b9f420e40a337b9e68c5ecec6 # Used for decryption only - bc17e7b413fd4720716a7633027f8cc4 # Used for encryption & decryption
From the above list of primary keys, the last key will always be used for encryption and all listed keys will be used for decryption until one of them works.
Decryption can be made more performant by storing the reference id of the encryption key in the encrypted message itself.
config.active_record.encryption.store_key_references = true
Now, the decryption key can be accessed directly without having to try out every key to find out which one works. Neat!
Note: Key rotation is not supported for deterministic encryption as of yet.
Built-in Encryption Key Providers
ActiveRecord Encryption further lets you control the key management strategies by allowing you to choose between different key providers.
Rails 7 comes with two built-in key providers to choose from:
DerivedSecretKeyProvider (default) :
Serves encryption & decryption keys derived from the provided passwords using PBKDF2.
The envelope encryption strategy uses a random secret to encrypt the plain text. The random secret is then encrypted using the encryption key and included in the encrypted message's headers. You can enable this by setting the following in your
config.active_record.encryption.key_provider = ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new
ActiveRecord Encryption has tons of other configurations which are worth checking out. You can refer to the Rails edge guides to find out more about them. Hope you find this blog helpful. Thank you for reading!