blind_index 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/LICENSE.txt +1 -1
- data/README.md +30 -14
- data/lib/blind_index.rb +18 -8
- data/lib/blind_index/extensions.rb +14 -2
- data/lib/blind_index/version.rb +1 -1
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f5aab7dbd2a784f6bcc2b464c14f76621c94886ec4764bc5e51f97ac74ae0d9
|
4
|
+
data.tar.gz: 104135feff81321860030ca46946cd5021cfb97fcc5a3953742ac61cee842cfd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b79e2f7597a572b4732f326bde1ce82de7941ffd3e97dc609834931cacb86a59c2ae6f5cc981cda0f6827956823bd01d910b56c5092409547a539a5db010e1b8
|
7
|
+
data.tar.gz: ba72cc3e13bb0fdd12e3502d1657e20074af745b41bb8fcb34df495e14fbddb36c3535e450950aa3ccb935a92c9cbbdcc70c7f1eaacccaba8c3fa358a3955470
|
data/CHANGELOG.md
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -6,11 +6,19 @@ Designed for use with [attr_encrypted](https://github.com/attr-encrypted/attr_en
|
|
6
6
|
|
7
7
|
Here’s a [full example](https://ankane.org/securing-user-emails-in-rails) of how to use it
|
8
8
|
|
9
|
+
Check out [this post](https://ankane.org/sensitive-data-rails) for more info on securing sensitive data with Rails
|
10
|
+
|
9
11
|
[![Build Status](https://travis-ci.org/ankane/blind_index.svg?branch=master)](https://travis-ci.org/ankane/blind_index)
|
10
12
|
|
11
13
|
## How It Works
|
12
14
|
|
13
|
-
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
|
15
|
+
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 to the value we’re searching and then perform a database search. This results in performant queries for exact matches. `LIKE` queries are not possible, but you can index expressions.
|
16
|
+
|
17
|
+
## Leakage
|
18
|
+
|
19
|
+
An important consideration in searchable encryption is leakage, which is information an attacker can gain. Blind indexing leaks that rows have the same value. If you use this for a field like last name, an attacker can use frequency analysis to predict the values. In an active attack where an attacker can control the input values, they can learn which other values in the database match.
|
20
|
+
|
21
|
+
Here’s a [great article](https://blog.cryptographyengineering.com/2019/02/11/attack-of-the-week-searchable-encryption-and-the-ever-expanding-leakage-function/) on leakage in searchable encryption. Blind indexing has the same leakage as deterministic encryption.
|
14
22
|
|
15
23
|
## Installation
|
16
24
|
|
@@ -31,26 +39,28 @@ add_column :users, :encrypted_email_bidx, :string
|
|
31
39
|
add_index :users, :encrypted_email_bidx
|
32
40
|
```
|
33
41
|
|
34
|
-
|
42
|
+
Next, generate a key
|
35
43
|
|
36
44
|
```ruby
|
37
|
-
|
38
|
-
blind_index :email, key: [ENV["EMAIL_BLIND_INDEX_KEY"]].pack("H*")
|
39
|
-
end
|
45
|
+
BlindIndex.generate_key
|
40
46
|
```
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
SecureRandom.hex(32)
|
46
|
-
```
|
48
|
+
Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production, and be sure this is different than the key you use for encryption. Keys don’t need to be hex-encoded, but it’s often easier to store them this way.
|
47
49
|
|
48
|
-
|
50
|
+
Here’s a key you can use in development
|
49
51
|
|
50
52
|
```sh
|
51
53
|
EMAIL_BLIND_INDEX_KEY=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
52
54
|
```
|
53
55
|
|
56
|
+
Add to your model
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class User < ApplicationRecord
|
60
|
+
blind_index :email, key: ENV["EMAIL_BLIND_INDEX_KEY"]
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
54
64
|
Backfill existing records
|
55
65
|
|
56
66
|
```ruby
|
@@ -212,8 +222,8 @@ And add to your model
|
|
212
222
|
|
213
223
|
```ruby
|
214
224
|
class User < ApplicationRecord
|
215
|
-
blind_index :email, key:
|
216
|
-
blind_index :email_v2, attribute: :email, key:
|
225
|
+
blind_index :email, key: ENV["EMAIL_BLIND_INDEX_KEY"]
|
226
|
+
blind_index :email_v2, attribute: :email, key: ENV["EMAIL_V2_BLIND_INDEX_KEY"]
|
217
227
|
end
|
218
228
|
```
|
219
229
|
|
@@ -230,7 +240,7 @@ Then update your model
|
|
230
240
|
|
231
241
|
```ruby
|
232
242
|
class User < ApplicationRecord
|
233
|
-
blind_index :email, bidx_attribute: :encrypted_email_v2_bidx, key:
|
243
|
+
blind_index :email, bidx_attribute: :encrypted_email_v2_bidx, key: ENV["EMAIL_V2_BLIND_INDEX_KEY"]
|
234
244
|
|
235
245
|
# remove this line after dropping column
|
236
246
|
self.ignored_columns = ["encrypted_email_bidx"]
|
@@ -254,6 +264,12 @@ Be sure to include the `inspect` at the end, or it won’t be encoded properly i
|
|
254
264
|
|
255
265
|
## Reference
|
256
266
|
|
267
|
+
Set default options in an initializer with:
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
BlindIndex.default_options[:algorithm] = :argon2
|
271
|
+
```
|
272
|
+
|
257
273
|
By default, blind indexes are encoded in Base64. Set a different encoding with:
|
258
274
|
|
259
275
|
```ruby
|
data/lib/blind_index.rb
CHANGED
@@ -11,16 +11,16 @@ module BlindIndex
|
|
11
11
|
class << self
|
12
12
|
attr_accessor :default_options
|
13
13
|
end
|
14
|
-
self.default_options = {
|
15
|
-
iterations: 10000,
|
16
|
-
algorithm: :pbkdf2_sha256,
|
17
|
-
insecure_key: false,
|
18
|
-
encode: true,
|
19
|
-
cost: {}
|
20
|
-
}
|
14
|
+
self.default_options = {}
|
21
15
|
|
22
16
|
def self.generate_bidx(value, key:, **options)
|
23
|
-
options =
|
17
|
+
options = {
|
18
|
+
iterations: 10000,
|
19
|
+
algorithm: :pbkdf2_sha256,
|
20
|
+
insecure_key: false,
|
21
|
+
encode: true,
|
22
|
+
cost: {}
|
23
|
+
}.merge(default_options).merge(options)
|
24
24
|
|
25
25
|
# apply expression
|
26
26
|
value = options[:expression].call(value) if options[:expression]
|
@@ -35,6 +35,11 @@ module BlindIndex
|
|
35
35
|
|
36
36
|
key = key.to_s
|
37
37
|
unless options[:insecure_key] && algorithm == :pbkdf2_sha256
|
38
|
+
# decode hex key
|
39
|
+
if key.encoding != Encoding::BINARY && key =~ /\A[0-9a-f]{64}\z/i
|
40
|
+
key = [key].pack("H*")
|
41
|
+
end
|
42
|
+
|
38
43
|
raise BlindIndex::Error, "Key must use binary encoding" if key.encoding != Encoding::BINARY
|
39
44
|
raise BlindIndex::Error, "Key must be 32 bytes" if key.bytesize != 32
|
40
45
|
end
|
@@ -90,6 +95,11 @@ module BlindIndex
|
|
90
95
|
end
|
91
96
|
end
|
92
97
|
end
|
98
|
+
|
99
|
+
def self.generate_key
|
100
|
+
require "securerandom"
|
101
|
+
SecureRandom.hex(32)
|
102
|
+
end
|
93
103
|
end
|
94
104
|
|
95
105
|
ActiveSupport.on_load(:active_record) do
|
@@ -7,7 +7,13 @@ module BlindIndex
|
|
7
7
|
if has_blind_indexes?
|
8
8
|
hash.each do |key, _|
|
9
9
|
if key.respond_to?(:to_sym) && (bi = klass.blind_indexes[key.to_sym]) && !new_hash[key].is_a?(ActiveRecord::StatementCache::Substitute)
|
10
|
-
|
10
|
+
value = new_hash.delete(key)
|
11
|
+
new_hash[bi[:bidx_attribute]] =
|
12
|
+
if value.is_a?(Array)
|
13
|
+
value.map { |v| BlindIndex.generate_bidx(v, bi) }
|
14
|
+
else
|
15
|
+
BlindIndex.generate_bidx(value, bi)
|
16
|
+
end
|
11
17
|
end
|
12
18
|
end
|
13
19
|
end
|
@@ -30,7 +36,13 @@ module BlindIndex
|
|
30
36
|
if has_blind_indexes?(klass)
|
31
37
|
hash.each do |key, _|
|
32
38
|
if key.respond_to?(:to_sym) && (bi = klass.blind_indexes[key.to_sym]) && !new_hash[key].is_a?(ActiveRecord::StatementCache::Substitute)
|
33
|
-
|
39
|
+
value = new_hash.delete(key)
|
40
|
+
new_hash[bi[:bidx_attribute]] =
|
41
|
+
if value.is_a?(Array)
|
42
|
+
value.map { |v| BlindIndex.generate_bidx(v, bi) }
|
43
|
+
else
|
44
|
+
BlindIndex.generate_bidx(value, bi)
|
45
|
+
end
|
34
46
|
end
|
35
47
|
end
|
36
48
|
end
|
data/lib/blind_index/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blind_index
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -98,16 +98,16 @@ dependencies:
|
|
98
98
|
name: sqlite3
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 1.3.0
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 1.3.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: scrypt
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -182,8 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
182
182
|
- !ruby/object:Gem::Version
|
183
183
|
version: '0'
|
184
184
|
requirements: []
|
185
|
-
|
186
|
-
rubygems_version: 2.7.6
|
185
|
+
rubygems_version: 3.0.3
|
187
186
|
signing_key:
|
188
187
|
specification_version: 4
|
189
188
|
summary: Securely search encrypted database fields
|