Modern Encryption for Rails

Lockbox

Encrypting sensitive data at the application-level is crucial for data security. Since writing Securing Sensitive Data in Rails, I haven’t been able to shake the feeling that encryption in Rails could be easier and cleaner.

To address this, I created a library called Lockbox. Here are some of the principles behind it.

Easy to Use, Hard to Misuse

Many cryptography mistakes happen during implementation. Lockbox provides good defaults and is designed to be hard to misuse. You don’t need to deal with initialization vectors and it only supports secure algorithms.

Use It Everywhere

Sensitive data can appear in many places, like database fields, file uploads, and strings. You shouldn’t need different libraries for each of these.

Lockbox can encrypt your data in all of these forms. It has built-in integrations with Active Record, Active Storage, and CarrierWave.

Zero Downtime Migrations

At some point, you may want to encrypt existing data. This should be easy to do, and most importantly, not require any downtime. Lockbox provides a single method you can use for this once your model is configured:

Lockbox.migrate(User)

No need to write one-off backfill scripts.

Maximum Compatibility

Encrypting attributes shouldn’t break existing code or libraries. To make this possible, methods like attribute_changed? and attribute_was should behave similarly regardless of whether or not an attribute is encrypted. Lockbox includes these methods in its test suite for maximum compatibility.

This allows features like Devise’s ability to send email change notifications to work when the email attribute is encrypted, which is an important measure to prevent account hijacking.

Devise.setup do |config|
  config.send_email_changed_notification = true
end

You can even query encrypted attributes thanks to the blind_index gem.

Modern Algorithms

Lockbox uses AES-GCM for authenticated encryption. It also supports XSalsa20 (thanks to Libsodium), which is recommended by some cryptographers.

Less Keys To Manage

It’s a good practice to use a different encryption key for each field to make it more difficult for attackers and to reduce the likelihood of a nonce collision. However, this can be burdensome for developers.

Instead, we can use a single master key and derive separate keys for each field from it. This approach is taken from CipherSweet, an encryption library for PHP and Node.js. Now developers can safely add encrypted fields without having to worry about generating and storing additional secrets.

You can still specify keys for certain fields if you prefer, but it’s no longer required. Lockbox also works with KMS Encrypted if you want to use a key management service to manage your keys.

Built-In Key Rotation

It’s good security hygiene to rotate your encryption keys from time-to-time. Lockbox makes this easy by allowing you to specify previous versions of keys and algorithm:

class User < ApplicationRecord
  encrypts :email, previous_versions: [{key: previous_key}]
end

New data is encrypted with the new key and algorithm, while older data can still be decrypted.

Cleaner Schema

attr_encrypted, the de facto encryption library for database fields, uses two fields for each encrypted attribute: one for the ciphertext and another for the initialization vector.

encrypted_email
encrypted_email_iv

However, it’s possible to store both in a single field for a cleaner schema.

email_ciphertext

Hybrid Cryptography

Hybrid cryptography allows certain servers to encrypt data without the ability to decrypt it. This can do a better job protecting data than symmetric cryptography when you can use it. Lockbox makes it just as easy to use hybrid cryptography.

class User < ApplicationRecord
  encrypts :email, algorithm: "hybrid", encryption_key: encryption_key, decryption_key: decryption_key
end

Updates

Since this post was originally published:

Summary

You’ve now seen what Lockbox brings to encryption for Rails. To summarize, it:

Try out Lockbox today.

Already use a library for encryption? No worries, it’s easy to migrate.

Published July 8, 2019


You might also enjoy

Securing User Emails in Rails with Lockbox

Hardening Devise

Lockbox: Now with Types


All code examples are public domain.
Use them however you’d like (licensed under CC0).