ActiveRecord Encryption in Rails 7+: Versleutel Gevoelige Data Zonder Je ORM te Verlaten
ActiveRecord Encryption is beschikbaar sinds Rails 7.0 en laat je specifieke databasekolommen versleutelen op applicatieniveau. Geen extra gems. Geen aparte encryptieservice. Je declareert welke attributen versleuteld worden, en Rails regelt de rest transparant.
Hier lees je hoe je het opzet, wat er onder de motorkap gebeurt, en welke productie-valkuilen de officiële guides overslaan.
Waarom Versleuteling op Applicatieniveau
Database-level encryptie (zoals PostgreSQL’s pgcrypto of AWS RDS encryption at rest) beschermt tegen iemand die de fysieke schijf of snapshot steelt. Het doet niets als een aanvaller database-credentials bemachtigt of SQL injection uitvoert — die leest gewoon plaintext, net als je app.
Applicatie-level encryptie betekent dat de data als ciphertext in de database staat. Zelfs met volledige SELECT * toegang ziet een aanvaller onleesbare tekens. Je Rails-app decodeert bij het lezen en versleutelt bij het schrijven, met sleutels die volledig buiten de database leven.
Dit is belangrijk voor PII-velden: e-mailadressen, telefoonnummers, BSN-nummers, API-tokens voor integraties. Het soort data waar toezichthouders en meldplicht-datalekken om geven.
Sleutels Instellen
Rails heeft drie sleutels nodig voor het encryptieschema. Genereer ze:
bin/rails db:encryption:init
Dit geeft zoiets als:
active_record_encryption:
primary_key: EGY8WhulUOXixybod7ZWwMIL68R9o5kC
deterministic_key: aPA5XyALhf75NNnMzaspW7akTfZp0lPY
key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz
Zet deze in config/credentials.yml.enc:
bin/rails credentials:edit
Plak het blok onder active_record_encryption. Rails pikt ze automatisch op.
Voor productie kun je ook omgevingsvariabelen gebruiken. In config/application.rb:
config.active_record.encryption.primary_key = ENV["AR_ENCRYPTION_PRIMARY_KEY"]
config.active_record.encryption.deterministic_key = ENV["AR_ENCRYPTION_DETERMINISTIC_KEY"]
config.active_record.encryption.key_derivation_salt = ENV["AR_ENCRYPTION_KEY_DERIVATION_SALT"]
Ik gebruik credentials voor single-server setups en environment variables bij deployment over meerdere nodes waar het synchroniseren van credential-bestanden rommelig wordt.
Je Eerste Kolom Versleutelen
Stel je hebt een users tabel met een email kolom die je wilt versleutelen. In je model:
class User < ApplicationRecord
encrypts :email, deterministic: true
encrypts :phone_number
end
Twee modi:
- Deterministisch (
deterministic: true): Dezelfde plaintext levert altijd dezelfde ciphertext op. Hiermee kun je de kolom doorzoeken metWHEREclausules. Gebruik dit voor velden waarop je moet zoeken — e-mailadressen, gebruikersnamen, rekeningnummers. - Niet-deterministisch (de standaard): Elke versleuteling produceert andere ciphertext. Veiliger (voorkomt frequentie-analyse aanvallen) maar je kunt deze kolommen niet doorzoeken. Gebruik dit voor velden die je alleen leest na het laden van het record — telefoonnummers, notities, API-sleutels.
Dat is alles voor het model. Geen migratie nodig als de kolom al bestaat als string of text type. Rails slaat de ciphertext op in de bestaande kolom.
Hoe de Ciphertext Eruitziet
Vóór versleuteling:
roger@example.com
Erna:
{"p":"nKQ8alYY2sCjBbx0","h":{"iv":"sNq5MRzjEUS4VfMR","at":"ntwU48aTKCYMxFCz8RPA3Q=="}}
Het is een JSON-envelope met de versleutelde payload, initialisatievector en authenticatietag. De kolom moet groot genoeg zijn — een string kolom (standaard 255 tekens) volstaat voor de meeste waarden, maar zeer lange plaintexts hebben wellicht text nodig.
Encrypted Data Doorzoeken
Deterministische encryptie maakt queries mogelijk die er volkomen normaal uitzien:
User.find_by(email: "roger@example.com")
# Rails versleutelt "roger@example.com" met de deterministische sleutel
# en voert uit: SELECT * FROM users WHERE email = '{"p":"nKQ8a..."}'
Dit werkt voor find_by, where, uniqueness-validaties en find_or_create_by. Vanuit je applicatiecode verandert er niets.
Wat niet werkt:
- LIKE queries:
User.where("email LIKE ?", "%example%")matcht niet. De ciphertext heeft geen relatie met het plaintext-patroon. - Database-level sortering:
ORDER BY emailsorteert op ciphertext, wat betekenisloos is. - Database-level constraints: Een
UNIQUEindex op de kolom werkt nog wel met deterministische encryptie (zelfde plaintext = zelfde ciphertext), maarCHECKconstraints op plaintext-waarden niet.
Bestaande Data Migreren
Als je encryptie toevoegt aan een kolom die al plaintext-data bevat, moet je de bestaande rijen opnieuw versleutelen:
class EncryptExistingEmails < ActiveRecord::Migration[7.1]
def up
User.find_each do |user|
user.encrypt
end
end
def down
User.find_each do |user|
user.decrypt
end
end
end
De encrypt methode leest elk attribuut, versleutelt het en slaat het op. Op een tabel met 100K rijen duurde dit ongeveer 45 seconden in mijn tests op PostgreSQL 16 met een standaard db.t3.medium RDS instance. Voor grotere tabellen, batch het met in_batches:
User.in_batches(of: 1000) do |batch|
batch.each(&:encrypt)
end
Draai dit in een maintenance window of achter een feature flag als je geen downtime kunt veroorloven. De kolom accepteert zowel plaintext als ciphertext tijdens de transitie — Rails detecteert wat het leest en handelt beide af.
Key Rotation
Sleutels raken gecompromitteerd. Compliance-frameworks vereisen periodieke rotatie. ActiveRecord Encryption handelt dit af met een sleutellijst:
# config/credentials.yml.enc
active_record_encryption:
primary_key:
- nieuwe_primary_key
- oude_primary_key
deterministic_key:
- nieuwe_deterministic_key
- oude_deterministic_key
key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz
Rails probeert decryptie met elke sleutel op volgorde. De eerste sleutel in de lijst wordt gebruikt voor nieuwe versleutelingen. Oude sleutels ontsleutelen bestaande data.
Na het toevoegen van de nieuwe sleutel, alles opnieuw versleutelen:
User.find_each(&:encrypt)
Dit leest met elke matchende sleutel en schrijft met de nieuwe primaire sleutel. Zodra alle rijen opnieuw versleuteld zijn, kun je de oude sleutel uit de lijst verwijderen.
Productie-valkuilen
Kolomgrootte
De JSON ciphertext-envelope voegt overhead toe. Een e-mail van 50 tekens wordt ongeveer 150 tekens versleuteld. Als je kolom varchar(100) is, loop je tegen truncatie aan. Controleer je kolomgroottes voordat je encryptie inschakelt.
Database Dumps en Imports
Als je je productiedatabase dumpt en importeert in staging, heeft de staging-omgeving dezelfde encryptiesleutels nodig om de data te lezen. Dit klinkt vanzelfsprekend, maar ik heb teams urenlang zien worstelen met onleesbare data na een verse import.
Console-toegang
rails console in productie ontsleutelt transparant. Iedereen met console-toegang kan versleutelde data lezen. Beperk productie console-toegang dienovereenkomstig.
Performance
Versleuteling en ontsleuteling voegen CPU-overhead toe per attribuuttoegang. In benchmarks op Ruby 3.3 met Rails 7.2:
- Eén attribuut versleutelen: ~0.02ms
- Eén attribuut ontsleutelen: ~0.015ms
- 1.000 records laden met 2 versleutelde attributen: voegt ~30ms totaal toe
Voor typische webrequests die een handvol records laden is dit onzichtbaar. Voor batchjobs die miljoenen rijen verwerken, telt het op.
Wanneer Niet Gebruiken
- Bestandsversleuteling: Gebruik Active Storage met een custom service of client-side encryptie.
- Wachtwoord-hashing: Blijf
has_secure_passwordmet bcrypt gebruiken. Encryptie is omkeerbaar; wachtwoordopslag hoort dat niet te zijn. - Kolommen in complexe SQL: Als je
LIKE,BETWEEN, joins of aggregaatfuncties nodig hebt, breekt encryptie die queries.
FAQ
Kan ik kolommen op bestaande tabellen versleutelen zonder downtime?
Ja. encrypts :column_name toevoegen aan het model is backward-compatible. Rails leest zowel plaintext als ciphertext uit de kolom. Je kunt dan geleidelijk rijen opnieuw versleutelen in achtergrondtaken terwijl de app normaal verkeer afhandelt.
Werkt ActiveRecord Encryption met PostgreSQL, MySQL en SQLite?
Het werkt met alle drie. De versleuteling gebeurt in Ruby vóórdat de waarde de database-adapter bereikt, dus de database-engine ziet nooit plaintext. Ik heb het specifiek in productie getest met PostgreSQL 15 en 16, maar de Rails test-suite dekt alle drie de adapters.
Welk encryptie-algoritme gebruikt Rails?
AES-256-GCM standaard, wat zowel vertrouwelijkheid als authenticatie (manipulatiedetectie) biedt. De key derivation gebruikt PBKDF2 met de salt die je configureert. AES-256-GCM is de standaardaanbeveling en wat NIST en de meeste compliance-frameworks verwachten.
Beïnvloeden versleutelde kolommen database-indexering?
Bij deterministische encryptie kun je de kolom indexeren en werkt de index correct omdat identieke plaintexts identieke ciphertexts opleveren. Bij niet-deterministische encryptie is een index technisch mogelijk maar nutteloos. Voeg alleen indexes toe aan deterministisch versleutelde kolommen.
About the Author
Roger Heykoop is een senior Ruby on Rails ontwikkelaar met 19+ jaar Rails ervaring en 35+ jaar ervaring in softwareontwikkeling. Hij is gespecialiseerd in Rails modernisering, performance optimalisatie, en AI-ondersteunde ontwikkeling.
Get in TouchRelated Articles
Need Expert Rails Development?
Let's discuss how we can help you build or modernize your Rails application with 19+ years of expertise
Schedule a Free Consultation