Storing sensitive data in plaintext can seriously harm your internet business if an attacker gets hold of the database. Encrypting data is also a GDPR friendly best practice. In this tutorial I will describe a simple way to securely encrypt, store, and decrypt data using built in Ruby on Rails helpers instead of external dependencies.
Avoid heavy Gem dependencies
attr_encrypted gem is a popular tool for storing encrypted data in Rails apps. The problem is that adding it to your application includes over 2k external lines of code. What’s worse is that the project has not been updated for several months at the time of writing.
Rails offers a handy ActiveSupport::MessageEncryptor
class, that hides away all the complexity of data encryption, and can be wrapped in a simple to use service object or reusable module.
Custom encryption service object
Let’s start with implementing a service object class doing the actual heavy lifting, but only exposing two straightforward public class methods encrypt
and decrypt
:
SECRET_KEY_BASE
and ENCRYPTION_SERVICE_SALT
somewhere safe otherwise, you would not be able to decrypt your secure data!You can use this code to generate a secure ENCRYPTION_SERVICE_SALT
value:
Now you can use the service directly in your models like that:
encrypted_api_token
database columnReusable module using metaprogramming
If you want to encrypt attributes across different models, you could simplify using the encryption service with a bit of metaprogramming magic:
Now you can include it in your ActiveRecord models. You can also use it to encrypt multiple attributes of a single model as long as there is a correct corresponding database column:
Searching by encrypted values
One caveat when it comes to encrypting data is they it is no longer searchable by plaintext value. In theory, you could decrypt objects one by one to find a match, but that would be terribly inefficient.
The described approach generates a different hash each time, even for the same values. It means that it must not be used for attributes that you’d like to use for searching.
Summary
This post only scratches a surface of data encryption in Rails, but this simple approach should cover many of the common use cases. I am using this method to encrypt Slack API tokens in my side project Abot.
Check out the official docs for more advanced uses of ActiveSupport::MessageEncryptor
like rotating keys and data expiry. With high-level helpers built in directly in Rails, you should always think twice before relying on external dependencies for security-related features.