BigBinary Blog

We write about Ruby on Rails, React.js, React Native, remote work, open source, engineering and design.

Rails 7.0 adds encryption to Active Record models

This blog is part of our Rails 7 series.

Before Rails 7.0, to add encryption on attributes of ActiveRecord models, we had to use third-party gems like lockbox which served the purpose but at the cost of an additional dependency.

Before we delve deeper, let's take a look at some terms related to encryption:

  1. Encrypt: to scramble a message in a way that only the intended person can extract the original message.
  2. Decrypt: to extract the original message from an encrypted one.
  3. Key: a string of characters used to encrypt/decrypt a message.
  4. Cipher: an algorithm used to encrypt and decrypt a message; RSA, Blowfish, and AES are some well-known examples.
  5. Deterministic: a process with guaranteed results; the sum of a set of numbers never changes.
  6. Non-deterministic: a process with unpredictable results; a roll of the dice can never be predicted.

Rails 7.0

Rails 7.0 adds encryption to attributes at the model level. By default, it supports encrypting serialized attribute types using the non-deterministic AES-GCM cipher.

To use this feature, we have to set the key_derivation_salt, primary_key, and deterministic_key variables in our environment file. These keys can be generated by running bin/rails db:encryption:init.

Let's enable encryption on the passport_number attribute of the User model.

1# app/models/user.rb
2class User < ApplicationRecord
3  encrypts :passport_number

In the above code, we have asked Rails to encrypt the passport_number attribute of User model when writing it to the database. Now, whenever we create a User, we will see the encrypted value of passport_number in the table, and the unencrypted value when we query using ActiveRecord.

1# rails console
2>> User.create name: "Akhil", passport_number: "BKLPG564"
3  TRANSACTION (0.1ms)  begin transaction
4  User Create (0.6ms)  INSERT INTO "users" ("name", "passport_number", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Akhil"], ["passport_number", "{\"p\":\"iOUC3ESsyxY=\",\"h\":{\"iv\":\"lDdCxI3LeoPv0hxZ\",\"at\":\"D50hElso0YvI6d8Li+l+lw==\"}}"], ["created_at", "2021-04-15 10:27:50.800729"], ["updated_at", "2021-04-15 10:27:50.800729"]]
5  TRANSACTION (1.5ms)  commit transaction
7>> User.last
8=> #<User id: 1, name: "Akhil", adhar: "BKLPG564", created_at: "2021-04-15 10:27:50.800729000 +0000", updated_at: "2021-04-15 10:27:50.800729000 +0000">

Under the hood, Rails 7 uses the EncryptableRecord concern to perform encryption and decryption when saving and retrieving values from the database.

Check out the pull request for more details of this encryption system. It is also documented in the Ruby on Rails guides.

Akhil Gautam in Rails, Rails 7
May 4, 2021

Subscribe to our newsletter