Encrypting data is straightforward in most Rails apps; however, once it’s encrypted, querying it can be complex. Rails 7 introduced deterministic encryption, which addresses this problem by allowing you to query encrypted fields while still keeping data secure.
What is Deterministic Encryption?
Deterministic encryption is an encryption method that produces the same ciphertext for identical plaintext inputs when using the same key. This predictability enables querying encrypted data, unlike standard encryption, which produces different ciphertexts for the same input each time.
Why use Deterministic Encryption?
There are cases where you need to query encrypted data. Here are some examples:
- Email lookups: Finding a user by their encrypted email address
- Social Security Numbers: Searching for records by encrypted SSN
- Phone numbers: Looking up customers by their encrypted contact information
With which standard (non-deterministic) encryption would these queries not be possible, since the same value encrypts differently each time.
How to use Deterministic Encryption
Deterministic encryption can be set up for a Rails app in the following steps:
1. Configure Active Record Encryption
First, we need to ensure Active Record Encryption is configured in the application:
# config/application.rb
config.active_record.encryption.primary_key = Rails.application.credentials.active_record_encryption[:primary_key]
config.active_record.encryption.deterministic_key = Rails.application.credentials.active_record_encryption[:deterministic_key]
config.active_record.encryption.key_derivation_salt = Rails.application.credentials.active_record_encryption[:key_derivation_salt]
Then we generate keys using:
rails db:encryption:init
2. Add Deterministic Encryption to a Model
We use the encrypts method with the deterministic: true option:
class User < ApplicationRecord
encrypts :email, deterministic: true
encrypts :ssn, deterministic: true
end
3. Query Encrypted Fields
Now you can query the encrypted fields just like regular fields:
# Find user by encrypted email
user = User.find_by(email: "user@example.com")
# Search by encrypted SSN
user = User.where(ssn: "123-45-6789").first
Rails automatically handles the encryption during the query, making it seamless.
Deterministic vs Non-Deterministic Encryption
Deterministic Encryption:
- Same plaintext always produces the same ciphertext
- Allows querying and indexing
- Slightly less secure because patterns can be detected
- Best for fields you need to search on
Non-Deterministic Encryption:
- Same plaintext produces a different ciphertext each time
- Cannot be queried or indexed
- More secure against pattern analysis
- Best for fields you only need to read/display
Example of using both:
class User < ApplicationRecord
encrypts :email, deterministic: true # Searchable
encrypts :credit_card_number # Not searchable, more secure
end
Security Considerations
While deterministic encryption allows querying, there are important security tradeoffs to consider:
When to Use Deterministic Encryption:
- Fields that need to be searchable or indexed
- Data that doesn’t reveal sensitive patterns when duplicated
- Applications where the convenience of querying outweighs the slightly reduced security
When NOT to Use Deterministic Encryption:
- Highly sensitive data like passwords (use proper password hashing instead)
- Data where pattern detection poses a significant risk
- Fields that don’t need to be queried
Conclusion
Deterministic encryption in Rails 8 provides a practical solution for encrypting sensitive data while maintaining the ability to query it. This comes with some security trade-offs compared to non-deterministic encryption, but it’s still very valuable for fields that need to be encrypted yet searchable.