As you may know, email addresses are a fairly common personal information and they are usually stored without encryption. If the haccker gains access to the database, then the email data will be compromised.
In this post, I will guide you an approach to be able to protect user email data. It is compatible with devise, a popular user authentication gem for Rails framework.
Strategy
To do the security of user email, we will do two things: encryption and blind indexing. Encryption will help us store data securely, and blind indexing will help us perform that encrypted data search.
Blind indexing works by calculating a hash of data. You may be familiar with data hashes such as MD5 and SHA1, which are one-way data encryption. Here we will use two-way data encryption, we will use the hash function to create a secret key and use key stretching to prevent brute force attacks. You can read more about blind indexing here .
I will use gem lockbox to conduct encryption and gem blind index to perform blind indexing.
Instructions
Let’s assume you have a User model with an email field.
First, add these 2 gems to the Gemfile file.
1 2 3 | gem 'lockbox' gem 'blind_index' |
And run bundle to install:
1 2 | bundle install |
Proceed to initializing a key:
1 2 | Lockbox.generate_key |
Store your key somewhere securely, usually to a Rails credential or to an environment variable ( dotenv is a pretty good gem for environment variables). And make sure you use different keys for different development environments.
Set the following environment variable to the value that your key was born above.
1 2 | LOCKBOX_MASTER_KEY=0000000000000000000000000000000000000000000000000000000000000000 |
Or you can create a config / initializers / lockbox.rb file with the content of
1 2 | Lockbox.master_key = Rails.application.credentials.lockbox_master_key |
Next, replace your email field with an encrypted version. Initialize a migration file as shown below
1 2 | rails generate migration add_email_ciphertext_to_users |
And add database changes to the file migration as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class AddEmailCiphertextToUsers < ActiveRecord::Migration[5.2] def change # encrypted data add_column :users, :email_ciphertext, :string # blind index add_column :users, :email_bidx, :string add_index :users, :email_bidx, unique: true # drop original here unless we have existing users remove_column :users, :email end end |
Proceed to migrate database:
1 2 | rails db:migrate |
Then add the following 2 lines to the User Model
1 2 3 4 5 | class User < ApplicationRecord encrypts :email blind_index :email end |
Then create a user and check it out
Existing Users
In case your system already exists data user before. We need to fill out the data before proceeding to delete the email field from the table. Think of encrypting old data first, then save it before deleting the email field.
1 2 3 4 5 | class User < ApplicationRecord encrypts :email, migrating: true blind_index :email, migrating: true end |
Then proceed to re-fill the data in the Rails console
1 2 | Lockbox.migrate(User) |
Then update the model to the desired state again.
1 2 3 4 5 6 7 8 | class User < ApplicationRecord encrypts :email blind_index :email # remove this line after dropping email column self.ignored_columns = ["email"] end |
Finally, proceed to drop the email column.
Logging
One more thing is that the email address will not be displayed during system operation. To do that, add the following to config / initializers / filterparameterlogging.rb :
1 2 | Rails.application.config.filter_parameters += [:email] |
Alternatively, we can use the logstop gem to filter anything in like an email address. Add gem to Gemfile
1 2 | gem 'logstop' |
And create a file config / initializers / logstop.rb with the content:
1 2 | Logstop.guard(Rails.logger) |
Summary
OK, now we know how to encrypt email and conduct data queries as needed. Alternatively you can apply this method to other data fields. However, this encoding will have a trade-off in performance, so consider it before executing it.