README.md in blind_index-0.3.3 vs README.md in blind_index-0.3.4
- old
+ new
@@ -2,61 +2,66 @@
Securely search encrypted database fields
Designed for use with [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted)
-Here’s a [full example](https://ankane.org/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
[![Build Status](https://travis-ci.org/ankane/blind_index.svg?branch=master)](https://travis-ci.org/ankane/blind_index)
## How It Works
-We use [this approach](https://www.sitepoint.com/how-to-search-on-securely-encrypted-database-fields/) by Scott Arciszewski. To summarize, we compute a keyed hash of the sensitive data and store it in a column. To query, we apply the keyed hash function (PBKDF2-HMAC-SHA256 by default) to the value we’re searching and then perform a database search. This results in performant queries for equality operations, while keeping the data secure from those without the key.
+We use [this approach](https://paragonie.com/blog/2017/05/building-searchable-encrypted-databases-with-php-and-sql) by Scott Arciszewski. To summarize, we compute a keyed hash of the sensitive data and store it in a column. To query, we apply the keyed hash function (PBKDF2-SHA256 by default) to the value we’re searching and then perform a database search. This results in performant queries for equality operations, while keeping the data secure from those without the key.
-## Getting Started
+## Installation
-Add these lines to your application’s Gemfile:
+Add this line to your application’s Gemfile:
```ruby
-gem 'attr_encrypted'
gem 'blind_index'
```
-Add columns for the encrypted data and the blind index
+## Getting Started
-```ruby
-# encrypted data
-add_column :users, :encrypted_email, :string
-add_column :users, :encrypted_email_iv, :string
+> Note: Your model should already be set up with attr_encrypted. The examples are for a `User` model with `attr_encrypted :email`. See the [full example](https://ankane.org/securing-user-emails-in-rails) if needed.
-# blind index
+Create a migration to add a column for the blind index
+
+```ruby
add_column :users, :encrypted_email_bidx, :string
add_index :users, :encrypted_email_bidx
```
And add to your model
```ruby
class User < ApplicationRecord
- 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). [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:
+We use an environment variable to store the key as a hex-encoded string ([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 it to source control.* This should be different than the key you use for encryption. You can generate a key in the Rails console with:
```ruby
SecureRandom.hex(32)
```
-For development, you can use these:
+For development, you can use this:
```sh
-EMAIL_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
EMAIL_BLIND_INDEX_KEY=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
```
+Backfill existing records
+
+```ruby
+User.find_each do |user|
+ user.compute_email_bidx
+ user.save!
+end
+```
+
And query away
```ruby
User.where(email: "test@example.org")
```
@@ -90,22 +95,31 @@
```ruby
add_column :users, :encrypted_email_ci_bidx, :string
add_index :users, :encrypted_email_ci_bidx
```
-And update your model
+Update your model
```ruby
class User < ApplicationRecord
blind_index :email, ...
blind_index :email_ci, attribute: :email, expression: ->(v) { v.downcase } ...
end
```
-Search with:
+Backfill existing records
```ruby
+User.find_each do |user|
+ user.compute_email_ci_bidx
+ user.save!
+end
+```
+
+And query away
+
+```ruby
User.where(email_ci: "test@example.org")
```
## Index Only
@@ -126,85 +140,67 @@
```ruby
class User < ApplicationRecord
attribute :initials
- # must come before blind_index method
+ # must come before the blind_index method so it runs first
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 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.
-
## Algorithms
-### PBKDF2-HMAC-SHA256
+### PBKDF2-SHA256
-The default hashing algorithm. [Key stretching](https://en.wikipedia.org/wiki/Key_stretching) increases the amount of time required to compute hashes, which slows down brute-force attacks. You can set the number of iterations with:
+The default hashing algorithm. [Key stretching](https://en.wikipedia.org/wiki/Key_stretching) increases the amount of time required to compute hashes, which slows down brute-force attacks.
+The default number of iterations is 10,000. For highly sensitive fields, set this to at least 100,000.
+
```ruby
class User < ApplicationRecord
- blind_index :email, iterations: 1000000, ...
+ blind_index :email, iterations: 100000, ...
end
```
-The default is `10000`. Changing this value requires you to recompute the blind index.
+> Changing this requires you to recompute the blind index.
-### scrypt
+### Argon2
-Add [scrypt](https://github.com/pbhogan/scrypt) to your Gemfile and use:
+Argon2 is the state-of-the-art algorithm and recommended for best security.
+To use it, add [argon2](https://github.com/technion/ruby-argon2) to your Gemfile and set:
+
```ruby
class User < ApplicationRecord
- blind_index :email, algorithm: :scrypt, ...
+ blind_index :email, algorithm: :argon2, ...
end
```
-Set the cost parameters with:
+The default cost parameters are `{t: 3, m: 12}`. For highly sensitive fields, set this to at least `{t: 4, m: 15}`.
```ruby
class User < ApplicationRecord
- blind_index :email, algorithm: :scrypt, cost: {n: 4096, r: 8, p: 1}, ...
+ blind_index :email, algorithm: :argon2, cost: {t: 4, m: 15}, ...
end
```
-### Argon2
+> Changing this requires you to recompute the blind index.
-Add [argon2](https://github.com/technion/ruby-argon2) to your Gemfile and use:
+The variant used is Argon2i.
-```ruby
-class User < ApplicationRecord
- blind_index :email, algorithm: :argon2, ...
-end
-```
+### Other
-Set the cost parameters with:
+scrypt is [also supported](docs/scrypt.md). Unless you have specific reasons to use it, go with Argon2 instead.
-```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
@@ -241,16 +237,37 @@
end
```
Finally, drop the old column.
+## Fixtures
+
+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.
+
## Reference
By default, blind indexes are encoded in Base64. Set a different encoding with:
```ruby
class User < ApplicationRecord
blind_index :email, encode: ->(v) { [v].pack("H*") }
+end
+```
+
+By default, blind indexes are 32 bytes. Set a smaller size with:
+
+```ruby
+class User < ApplicationRecord
+ blind_index :email, size: 16
end
```
## Alternatives