Rails 7 adds encryption to ActiveRecord


Before Rails 7, we used a lot of gems like attr_encrypted to encrypt and decrypt data in Active record models.

Let’s take an example of a User model, where we want to store the email in an encrypted format.

class User < ApplicationRecord
  validates_presence_of :email
end

Previously, using attr_encrypted gem we would do something like this:

class User < ApplicationRecord
  validates_presence_of :email
  attr_encrypted :email, key: :encryption_key
end

This increases the dependency on third-party gem for critical functionality like encryption.

To handle this problem, Rails 7 adds encrypted attributes to ActiveRecord models.

Basic setup

  • Firstly, we need to add some keys to our Rails credential file. Run the following command to generate the key set:
$ bin/rails db:encryption:init
  • Specify the attributes we need to encrypt. This happens at the model level:
# app/models/user.rb
class User < ApplicationRecord
  encrypts :email
  validates_presence_of :email
end

Usage

The library will transparently encrypt email before saving it into the database, and will decrypt it when retrieving its value.

User.create email: "sam@welcome.com"

# INSERT INTO `users` (`email`) VALUES ('{\"p\":\"n7J0/ol+a7DRMeaE\",\"h\":{\"iv\":\"DXZMDWUKfp3bg/Yu\",\"at\":\"X1/YjMHbHD4talgF9dt61A==\"}}')

Deterministic and Non-Deterministic encryption

  • By default, ActiveRecord Encryption uses a non-deterministic approach for encryption. That means encrypting the same email twice will result in 2 different ciphertexts. It is better for security purposes, but it makes querying the database impossible. So we can use the deterministic approach to resolve this issue.
# app/models/user.rb
class User < ApplicationRecord
  encrypts :email, deterministic: true
  encrypts :phone_number
end

After this if we query the model normally like:

User.find_by(email: "sam@welcome.com")
User.find_by(phone_number: "9911223344")

Since we did not set deterministic: true for the phone_number attribute, the query fails to find the user.

Custom Encryption methods

Rails 7 uses the EncryptableRecord concern to perform encryption and decryption when saving and retrieving values from the database. The main components of an encryption system are:

  • Encryptor - responsible for encrypting/decrypting data.
  • Cipher - the encryption algorithm (Aes 256 GCM)
  • KeyProvider - serves encryption and decryption keys
  • MessageSerializer - in charge of serializing and deserializing encrypted Message.

These components can be customized according to the needs by modifying the respective settings in the config file like:

config.active_record.encryption.encryptor = MyEncryptor.new

For more details, refer to this pull request.