Lindsay Holmwood

Lindsay Holmwood

June 23, 2022

Searching your encrypted data with ActiveRecord Encryption + ActiveStash

Encrypt your data with ActiveRecord Encryption. Perform fully encrypted searches with ActiveStash. It's really that easy.

CipherStash - Searching your encrypted data with ActiveRecord Encryption + ActiveStash

Rails 7 introduced ActiveRecord Encryption to Rails. This brings powerful application-level encryption to ActiveRecord models, and helps developers to seamlessly encrypt and decrypt specific database fields.

ActiveRecord Encryption follows the footsteps of application-level encryption plugins for ActiveRecord like Lockbox, attr_encrypted, CryptKeeper , and Strongbox.

However, there is one huge drawback to using application-level encryption:

When you encrypt your data, you lose the ability to search.

For most developers, losing the ability to search their data isn’t worth the security gain.

ActiveStash + CipherStash eliminates this tradeoff.

With ActiveStash, you can encrypt your data inside Rails, but still search it.

Most importantly, the index that ActiveStash searches is fully encrypted, due to CipherStash’s groundbreaking searchable encryption technology.

CipherStash never sees your keys or plaintext when indexing or searching your data.

ActiveStash helps you do range queries, ordering, and full text search on encrypted data.

When your records are protected by ActiveRecord Encryption and you try and do a range query, you hit a roadblock instantly:

> User.where("dob > ?", 20.years.ago) => []

ActiveStash gives you range queries back:

> User.query { |q| q.dob > 50.years.ago } => [#<User id: 19, first_name: "Grace", ...>, #<User id: 20, first_name: "Ada", ... >]

When your records are protected by ActiveRecord Encryption and you try to order your results, yet another roadblock:

> User.order(:name) => [#<User id: 905456733, name: "Margaret Hamilton">, #<User id: 258552901, name: "Anita Borg">, #<User id: 370882803, name: "Grace Hopper">, #<User id: 134100488, name: "Radia Perlman">, #<User id: 225478506, name: "Ada Lovelace">, #<User id: 391913150, name: "Frances Allen">]

On first glance, the records don't appear to be ordered. But if you look closer, the data is sorted — but by the underlying ciphertexts, instead of the actual plaintext data.

sqlite> SELECT name FROM users ORDER BY name; name {"p":"Q0RgizFz7xhF5CRC8GhxB0E=","h":{"iv":"6BGlZdALhMgRh0cl","at":"Dk9A2veUedsFPwLbxUm3Iw=="}} {"p":"bBko5ZVXQFk3lg==","h":{"iv":"gHrORcZV0jfHUqTU","at":"VyS3Gh/yOGqQw+erC5QvzQ=="}} {"p":"cOs/2cNA+YWe0p70","h":{"iv":"irn45NHdZ7qvI4Un","at":"T0EEKMMGGHram9p4Qu2Fsw=="}} {"p":"h8UOa4MZwmlDVnYtQQ==","h":{"iv":"IVG6+IAjxV801XBD","at":"htxQMmQNi+rd5PzlAXa28Q=="}} {"p":"kCEc2m+ZSS/lsb2c","h":{"iv":"xiCC1UUF+VXHt6PC","at":"Zt+gOLffNMHMQvY9Xe7Zmg=="}} {"p":"kyjwkJsciDqs9h8pNA==","h":{"iv":"eiDWZGtUL0bcLlzF","at":"JndrnAn/QLEDcqiAhoRYjA=="}}

ActiveStash gives you ordering back:

> User.query.order(:name) => [#<User id: 225478506, name: "Ada Lovelace">, #<User id: 258552901, name: "Anita Borg"> #<User id: 391913150, name: "Frances Allen"> #<User id: 370882803, name: "Grace Hopper">, #<User id: 905456733, name: "Margaret Hamilton">, #<User id: 134100488, name: "Radia Perlman">]

When your records are protected by ActiveRecord Encryption and you try to do a full text search, you hit another dead end:

> User.where("name LIKE '%ace%'") => []

ActiveStash gives you encrypted full text search, with comparable performance to what you get from your plain text full text search that your database has right now:

> User.query { |q| q.name =~ "ace" } => [#<User id: 225478506, name: "Ada Lovelace">, #<User id: 370882803, name: "Grace Hopper">]

Best yet, ActiveStash works with any database that ActiveRecord supports: PostgreSQL, MySQL and its ilk, SQL Server, SQLite, and Oracle.

ActiveStash keeps your encrypted data searchable, regardless of the underlying database.

Add ActiveStash to your app in under 5 minutes.

  1. Add ActiveStash to your Gemfile:
gem "active_stash"
  1. Install the new dependencies:
$ bundle install
  1. Create a CipherStash account (which will provision you a workspace) and then login:
$ rake active_stash:signup

Note the workspace ID you are issued at the end.

Note: If you are using zsh you may need to escape the brackets

$ rake active_stash:login\['WorkspaceId'\]
  1. Any model you use with ActiveStash::Search needs to have a stash_id column, to link search results back to database records.

For example, to add a stash_id column to the database table for the User model, add the below migration:

$ rails generate migration AddStashIdToUser stash_id:string:index $ rails db:migrate
  1. Add the ActiveStash::Search mixin to your User model, and declare what fields are searchable:
# app/models/user.rb class User < ApplicationRecord include ActiveStash::Search # Previously added application-level encryption encrypts :name, :email encrypts :dob, type: :date # Fields that will be indexed into CipherStash stash_index :name, :email, :dob end
  1. Reindex your existing data with ActiveStash
$ rails active_stash:reindexall
  1. Query a user record:
> User.where(name: "Grace Hopper") => [] # No records, because the database isn't searchable. > User.query(email: "grace@example.com") => [#<User id: 370882803, name: "Grace Hopper", ...>]

Next steps

ActiveStash is free to use for indexing up to 10,000 record per month, and returning 100,000 records in searches per month.

About the Author

Got sensitive data you need to secure?

Sign up for free

No credit card required.

Latest Posts

View All Articles