Why encrypt in the app, not just at rest

Free preview 3 min to read

A new kind of migration question

Every migrations course so far asked how tables relate to each other, how safe an operation is on a live table, or how to order several changes. This course asks something else: not how a column is shaped or related, but what's safe to store in it as plain text, and what changes once the answer is "nothing".

Two different things both called "encryption"

Many hosting setups encrypt a database's disk at rest, so the physical disk can't be read without its own key. That protects against one scenario: someone stealing the drive, or a backup file, without the credentials to use it. It does nothing against the other ways data leaks: a leaked database password, an overly broad SELECT * in a support tool, a pg_dump file sent to the wrong person, a compromised admin account. Each of these hands over readable text, because disk encryption lives below the database. PostgreSQL never knows it's there, and neither does anyone who can log in and run a query.

ActiveRecord::Encryption, this course's subject, works at a different layer: inside the Rails application, before a value is written and after it's read back. A column encrypted this way stores ciphertext that PostgreSQL can't read, because Rails, not the database, holds the key. Anyone with a live database connection and a plain SELECT sees unreadable bytes, whether that's an attacker, a careless internal tool or a read replica with too much access. The threat model widens from "someone stole the hardware" to "someone has ordinary read access to data they were never meant to see".

What this app could encrypt

No model in this app calls encrypts today. Its Partners::Inquiry model, with email, company, contact_name, message and notes, has the shape of data this course is for: contact details from a real person, submitted through a public form. The later lessons use Partners::Inquiry's columns as the running example of what encryption would look like.

One column never appears in this course: Identity::User#password_digest. It's already protected by has_secure_password with BCrypt, a one-way hash that's never meant to be read back. Encryption assumes the opposite, a value that has to be retrieved and used later. Encrypting an already-hashed password would solve a problem that column doesn't have.

Key takeaways

  • Disk encryption at rest protects against theft of the disk or a backup file. It does nothing once someone has an ordinary, authenticated database connection, because PostgreSQL never knows it exists.
  • ActiveRecord::Encryption works one layer up, inside the Rails app, so a live SELECT against the database returns ciphertext instead of plain text.
  • No model in this app calls encrypts today. Partners::Inquiry (email, company, contact_name, message, notes) is this course's running example of the kind of data the feature is for.
  • Encryption and hashing solve different problems. Identity::User#password_digest (BCrypt, one-way) is out of scope, because a value that's never meant to be read back doesn't need a feature built for values that are.
Lesson 1 of 7 · free

Keep going with lesson 2

Setup: db:encryption:init and credentials

Generates three keys with bin/rails db:encryption:init, stores them in credentials and explains the role of each.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...