README.md in blind_index-0.3.2 vs README.md in blind_index-0.3.3

- old
+ new

@@ -2,11 +2,11 @@ Securely search encrypted database fields Designed for use with [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted) -Here’s a [full example](https://shorts.dokkuapp.com/securing-user-emails-in-rails/) of how to use it with Devise +Here’s a [full example](https://ankane.org/securing-user-emails-in-rails) of how to use it with Devise [![Build Status](https://travis-ci.org/ankane/blind_index.svg?branch=master)](https://travis-ci.org/ankane/blind_index) ## How It Works @@ -40,11 +40,11 @@ attr_encrypted :email, key: [ENV["EMAIL_ENCRYPTION_KEY"]].pack("H*") blind_index :email, key: [ENV["EMAIL_BLIND_INDEX_KEY"]].pack("H*") end ``` -We use environment variables to store the keys as hex-encoded strings ([dotenv](https://github.com/bkeepers/dotenv) is great for this). *Do not commit them to source control.* Generate one key for encryption and one key for hashing. You can generate keys in the Rails console with: +We use environment variables to store the keys as hex-encoded strings ([dotenv](https://github.com/bkeepers/dotenv) is great for this). [Here’s an explanation](https://ankane.org/encryption-keys) of why `pack` is used. *Do not commit them to source control.* Generate one key for encryption and one key for hashing. You can generate keys in the Rails console with: ```ruby SecureRandom.hex(32) ``` @@ -116,16 +116,40 @@ attribute :email blind_index :email, ... end ``` +*Requires ActiveRecord 5.1+* + +## Multiple Columns + +You can also use virtual attributes to index data from multiple columns: + +```ruby +class User < ApplicationRecord + attribute :initials + + # must come before blind_index method + before_validation :set_initials, if: -> { changes.key?(:first_name) || changes.key?(:last_name) } + blind_index :initials, ... + + def set_initials + self.initials = "#{first_name[0]}#{last_name[0]}" + end +end +``` + +*Requires ActiveRecord 5.1+* + ## Fixtures -You can use blind indexes in fixtures with: +You can use encrypted attributes and blind indexes in fixtures with: ```yml test_user: + encrypted_email: <%= User.encrypt_email("test@example.org", iv: Base64.decode64("0000000000000000")) %> + encrypted_email_iv: "0000000000000000" encrypted_email_bidx: <%= User.compute_email_bidx("test@example.org").inspect %> ``` Be sure to include the `inspect` at the end, or it won’t be encoded properly in YAML. @@ -176,9 +200,49 @@ ```ruby class User < ApplicationRecord blind_index :email, algorithm: :argon2, cost: {t: 3, m: 12}, ... end ``` + +## Key Rotation + +To rotate keys without downtime, add a new column: + +```ruby +add_column :users, :encrypted_email_v2_bidx, :string +add_index :users, :encrypted_email_v2_bidx +``` + +And add to your model + +```ruby +class User < ApplicationRecord + blind_index :email, key: [ENV["EMAIL_BLIND_INDEX_KEY"]].pack("H*") + blind_index :email_v2, attribute: :email, key: [ENV["EMAIL_V2_BLIND_INDEX_KEY"]].pack("H*") +end +``` + +Backfill the data + +```ruby +User.find_each do |user| + user.compute_email_v2_bidx + user.save! +end +``` + +Then update your model + +```ruby +class User < ApplicationRecord + blind_index :email, bidx_attribute: :encrypted_email_v2_bidx, key: [ENV["EMAIL_V2_BLIND_INDEX_KEY"]].pack("H*") + + # remove this line after dropping column + self.ignored_columns = ["encrypted_email_bidx"] +end +``` + +Finally, drop the old column. ## Reference By default, blind indexes are encoded in Base64. Set a different encoding with: