powerhome-attr_encrypted 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +67 -0
  4. data/CHANGELOG.md +98 -0
  5. data/Gemfile +3 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +465 -0
  8. data/Rakefile +25 -0
  9. data/attr_encrypted.gemspec +63 -0
  10. data/certs/saghaulor.pem +21 -0
  11. data/checksum/attr_encrypted-3.0.0.gem.sha256 +1 -0
  12. data/checksum/attr_encrypted-3.0.0.gem.sha512 +1 -0
  13. data/checksum/attr_encrypted-3.0.1.gem.sha256 +1 -0
  14. data/checksum/attr_encrypted-3.0.1.gem.sha512 +1 -0
  15. data/checksum/attr_encrypted-3.0.2.gem.sha256 +1 -0
  16. data/checksum/attr_encrypted-3.0.2.gem.sha512 +1 -0
  17. data/checksum/attr_encrypted-3.0.3.gem.sha256 +1 -0
  18. data/checksum/attr_encrypted-3.0.3.gem.sha512 +1 -0
  19. data/checksum/attr_encrypted-3.1.0.gem.sha256 +1 -0
  20. data/checksum/attr_encrypted-3.1.0.gem.sha512 +1 -0
  21. data/lib/attr_encrypted.rb +473 -0
  22. data/lib/attr_encrypted/adapters/active_record.rb +157 -0
  23. data/lib/attr_encrypted/adapters/data_mapper.rb +24 -0
  24. data/lib/attr_encrypted/adapters/sequel.rb +16 -0
  25. data/lib/attr_encrypted/version.rb +19 -0
  26. data/test/active_record_test.rb +365 -0
  27. data/test/attr_encrypted_test.rb +490 -0
  28. data/test/compatibility_test.rb +109 -0
  29. data/test/data_mapper_test.rb +59 -0
  30. data/test/legacy_active_record_test.rb +120 -0
  31. data/test/legacy_attr_encrypted_test.rb +300 -0
  32. data/test/legacy_compatibility_test.rb +95 -0
  33. data/test/legacy_data_mapper_test.rb +57 -0
  34. data/test/legacy_sequel_test.rb +54 -0
  35. data/test/run.sh +12 -0
  36. data/test/sequel_test.rb +55 -0
  37. data/test/test_helper.rb +61 -0
  38. metadata +294 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 225b9354cf7318310fca332845cd11736b9a41916188ceb47b0631c6248894a4
4
+ data.tar.gz: 7de216dc4eacec3cb6893c9dd11f8bf2f6598caaebadef6b46133d31bd384754
5
+ SHA512:
6
+ metadata.gz: 25ba6cab8717767ce41e28651c571921a5e352ea1b7f1a272facd0d1bff9d502a802ebba0a21fba617b1adfba76f4e2cc7c359988065a0f6c2b76cad37f0f40a
7
+ data.tar.gz: 334094f5193fdc3a96362a2b8779a6afb331a8c4fa7c15577d74c33b7b57001722a431f12e1d91cd4734f7ff7bde6efcb4f90be783835878a6e2f08e7fce5836
@@ -0,0 +1,6 @@
1
+ .bundle
2
+ .DS_Store
3
+ .ruby-version
4
+ pkg
5
+ Gemfile.lock
6
+ coverage
@@ -0,0 +1,67 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.0
6
+ - 2.1
7
+ - 2.2.2
8
+ - 2.3.0
9
+ - 2.4.0
10
+ - 2.5.0
11
+ - rbx
12
+ env:
13
+ - ACTIVERECORD=3.0.0
14
+ - ACTIVERECORD=3.1.0
15
+ - ACTIVERECORD=3.2.0
16
+ - ACTIVERECORD=4.0.0
17
+ - ACTIVERECORD=4.1.0
18
+ - ACTIVERECORD=4.2.0
19
+ - ACTIVERECORD=5.0.0
20
+ - ACTIVERECORD=5.1.1
21
+ - ACTIVERECORD=5.2.0
22
+ matrix:
23
+ exclude:
24
+ - rvm: 2.0
25
+ env: ACTIVERECORD=5.0.0
26
+ - rvm: 2.0
27
+ env: ACTIVERECORD=5.1.1
28
+ - rvm: 2.0
29
+ env: ACTIVERECORD=5.2.0
30
+ - rvm: 2.1
31
+ env: ACTIVERECORD=5.0.0
32
+ - rvm: 2.1
33
+ env: ACTIVERECORD=5.1.1
34
+ - rvm: 2.1
35
+ env: ACTIVERECORD=5.2.0
36
+ - rvm: 2.4.0
37
+ env: ACTIVERECORD=3.0.0
38
+ - rvm: 2.4.0
39
+ env: ACTIVERECORD=3.1.0
40
+ - rvm: 2.4.0
41
+ env: ACTIVERECORD=3.2.0
42
+ - rvm: 2.4.0
43
+ env: ACTIVERECORD=4.0.0
44
+ - rvm: 2.4.0
45
+ env: ACTIVERECORD=4.1.0
46
+ - rvm: 2.5.0
47
+ env: ACTIVERECORD=3.0.0
48
+ - rvm: 2.5.0
49
+ env: ACTIVERECORD=3.1.0
50
+ - rvm: 2.5.0
51
+ env: ACTIVERECORD=3.2.0
52
+ - rvm: 2.5.0
53
+ env: ACTIVERECORD=4.0.0
54
+ - rvm: 2.5.0
55
+ env: ACTIVERECORD=4.1.0
56
+ - rvm: rbx
57
+ env: ACTIVERECORD=5.0.0
58
+ - rvm: rbx
59
+ env: ACTIVERECORD=5.1.1
60
+ - rvm: rbx
61
+ env: ACTIVERECORD=5.2.0
62
+ allow_failures:
63
+ - rvm: rbx
64
+ fast_finish: true
65
+ addons:
66
+ code_climate:
67
+ repo_token: a90435ed4954dd6e9f3697a20c5bc3754f67d94703f870e8fc7b00f69f5b2d06
@@ -0,0 +1,98 @@
1
+ # attr_encrypted #
2
+
3
+ ## 3.1.0 ##
4
+ * Added: Abitilty to encrypt empty values. (@tamird)
5
+ * Added: MIT license
6
+ * Added: MRI 2.5.x support (@saghaulor)
7
+ * Fixed: No long generate IV and salt if value is empty, unless :allow_empty_value options is passed. (@saghaulor)
8
+ * Fixed: Only generate IV and salt when :if and :unless options evaluate such that encryption should be performed. (@saghaulor)
9
+ * Fixed: Private methods are correctly evaluated as options. (@saghaulor)
10
+ * Fixed: Mark virtual attributes for Rails 5.x compatibility (@grosser)
11
+ * Fixed: Only check empty on strings, allows for encrypting non-string type objects
12
+ * Fixed: Fixed how accessors for db columns are defined in the ActiveRecord adapter, preventing premature definition. (@nagachika)
13
+
14
+ ## 3.0.3 ##
15
+ * Fixed: attr_was would decrypt the attribute upon every call. This is inefficient and introduces problems when the options change between decrypting an old value and encrypting a new value; for example, when rotating the encryption key. As such, the new approach caches the decrypted value of the old encrypted value such that the old options are no longer needed. (@johnny-lai) (@saghaulor)
16
+
17
+ ## 3.0.2 ##
18
+ * Changed: Removed alias_method_chain for compatibility with Rails v5.x (@grosser)
19
+ * Changed: Updated Travis build matrix to include Rails 5. (@saghaulor) (@connorshea)
20
+ * Changed: Removed `.silence_stream` from tests as it has been removed from Rails 5. (@sblackstone)
21
+
22
+ ## 3.0.1 ##
23
+ * Fixed: attr_was method no longer calls undefined methods. (@saghaulor)
24
+
25
+ ## 3.0.0 ##
26
+ * Changed: Updated gemspec to use Encryptor v3.0.0. (@saghaulor)
27
+ * Changed: Updated README with instructions related to moving from v2.0.0 to v3.0.0. (@saghaulor)
28
+ * Fixed: ActiveModel::Dirty methods in the ActiveRecord adapter. (@saghaulor)
29
+
30
+ ## 2.0.0 ##
31
+ * Added: Now using Encryptor v2.0.0 (@saghaulor)
32
+ * Added: Options are copied to the instance. (@saghaulor)
33
+ * Added: Operation option is set during encryption/decryption to allow options to be evaluated in the context of the current operation. (@saghaulor)
34
+ * Added: IV and salt can be conditionally encoded. (@saghaulor)
35
+ * Added: Changelog! (@saghaulor)
36
+ * Changed: attr_encrypted no longer extends object, to use with PORO extend your class, all supported ORMs are already extended. (@saghaulor)
37
+ * Changed: Salt is now generated with more entropy. (@saghaulor)
38
+ * Changed: The default algorithm is now `aes-256-gcm`. (@saghaulor)
39
+ * Changed: The default mode is now `:per_attribute_iv`' (@saghaulor)
40
+ * Changed: Extracted class level default options hash to a private method. (@saghaulor)
41
+ * Changed: Dynamic finders only work with `:single_iv_and_salt` mode. (@saghaulor)
42
+ * Changed: Updated documentation to include v2.0.0 changes and 'things to consider' section. (@saghaulor)
43
+ * Fixed: All options are evaluated correctly. (@saghaulor)
44
+ * Fixed: IV is generated for every encryption operation. (@saghaulor)
45
+ * Deprecated: `:single_iv_and_salt` and `:per_attribute_iv_and_salt` modes are deprecated and will be removed in the next major release. (@saghaulor)
46
+ * Deprecated: Dynamic finders via `method_missing` is deprecated and will be removed in the next major release. (@saghaulor)
47
+ * Removed: Support for Ruby < 2.x (@saghaulor)
48
+ * Removed: Support for Rails < 3.x (@saghaulor)
49
+ * Removed: Unnecessary use of `alias_method` from ActiveRecord adapter. (@saghaulor)
50
+
51
+ ## 1.4.0 ##
52
+ * Added: ActiveModel::Dirty#attribute_was (@saghaulor)
53
+ * Added: ActiveModel::Dirty#attribute_changed? (@mwean)
54
+
55
+ ## 1.3.5 ##
56
+ * Changed: Fixed gemspec to explicitly depend on Encryptor v1.3.0 (@saghaulor)
57
+ * Fixed: Evaluate `:mode` option as a symbol or proc. (@cheynewallace)
58
+
59
+ ## 1.3.4 ##
60
+ * Added: ActiveRecord::Base.reload support. (@rcook)
61
+ * Fixed: ActiveRecord adapter no longer forces attribute hashes to be string-keyed. (@tamird)
62
+ * Fixed: Mass assignment protection in ActiveRecord 4. (@tamird)
63
+ * Changed: Now using rubygems over https. (@tamird)
64
+ * Changed: Let ActiveRecord define attribute methods. (@saghaulor)
65
+
66
+ ## 1.3.3 ##
67
+ * Added: Alias attr_encryptor and attr_encrpted. (@Billy Monk)
68
+
69
+ ## 1.3.2 ##
70
+ * Fixed: Bug regarding strong parameters. (@S. Brent Faulkner)
71
+ * Fixed: Bug regarding loading per instance IV and salt. (@S. Brent Faulkner)
72
+ * Fixed: Bug regarding assigning nil. (@S. Brent Faulkner)
73
+ * Added: Support for protected attributes. (@S. Brent Faulkner)
74
+ * Added: Support for ActiveRecord 4. (@S. Brent Faulkner)
75
+
76
+ ## 1.3.1 ##
77
+ * Added: Support for Rails 2.3.x and 3.1.x. (@S. Brent Faulkner)
78
+
79
+ ## 1.3.0 ##
80
+ * Fixed: Serialization bug. (@Billy Monk)
81
+ * Added: Support for :per_attribute_iv_and_salt mode. (@rcook)
82
+ * Fixed: Added dependencies to gemspec. (@jmazzi)
83
+
84
+ ## 1.2.1 ##
85
+ * Added: Force encoding when not marshaling. (@mosaicxm)
86
+ * Fixed: Issue specifying multiple attributes on the same line. (@austintaylor)
87
+ * Added: Typecasting to String before encryption (@shuber)
88
+ * Added: `"#{attribute}?"` method. (@shuber)
89
+
90
+ ## 1.2.0 ##
91
+ * Changed: General code refactoring (@shuber)
92
+
93
+ ## 1.1.2 ##
94
+ * No significant changes
95
+
96
+ ## 1.1.1 ##
97
+ * Changled: Updated README. (@shuber)
98
+ * Added: `before_type_cast` alias to ActiveRecord adapter. (@shuber)
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Sean Huber - shuber@huberry.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,465 @@
1
+ # attr_encrypted
2
+ [![Build Status](https://secure.travis-ci.org/attr-encrypted/attr_encrypted.svg)](https://travis-ci.org/attr-encrypted/attr_encrypted) [![Test Coverage](https://codeclimate.com/github/attr-encrypted/attr_encrypted/badges/coverage.svg)](https://codeclimate.com/github/attr-encrypted/attr_encrypted/coverage) [![Code Climate](https://codeclimate.com/github/attr-encrypted/attr_encrypted/badges/gpa.svg)](https://codeclimate.com/github/attr-encrypted/attr_encrypted) [![Gem Version](https://badge.fury.io/rb/attr_encrypted.svg)](https://badge.fury.io/rb/attr_encrypted) [![security](https://hakiri.io/github/attr-encrypted/attr_encrypted/master.svg)](https://hakiri.io/github/attr-encrypted/attr_encrypted/master)
3
+
4
+ Power's version of the attr_encrypted gem: https://github.com/attr-encrypted/attr_encrypted
5
+
6
+ --
7
+
8
+ Generates attr_accessors that transparently encrypt and decrypt attributes.
9
+
10
+ It works with ANY class, however, you get a few extra features when you're using it with `ActiveRecord`, `DataMapper`, or `Sequel`.
11
+
12
+
13
+ ## Installation
14
+
15
+ Add attr_encrypted to your gemfile:
16
+
17
+ ```ruby
18
+ gem "attr_encrypted", "~> 3.1.0"
19
+ ```
20
+
21
+ Then install the gem:
22
+
23
+ ```bash
24
+ bundle install
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ If you're using an ORM like `ActiveRecord`, `DataMapper`, or `Sequel`, using attr_encrypted is easy:
30
+
31
+ ```ruby
32
+ class User
33
+ attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'
34
+ end
35
+ ```
36
+
37
+ If you're using a PORO, you have to do a little bit more work by extending the class:
38
+
39
+ ```ruby
40
+ class User
41
+ extend AttrEncrypted
42
+ attr_accessor :name
43
+ attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'
44
+
45
+ def load
46
+ # loads the stored data
47
+ end
48
+
49
+ def save
50
+ # saves the :name and :encrypted_ssn attributes somewhere (e.g. filesystem, database, etc)
51
+ end
52
+ end
53
+
54
+ user = User.new
55
+ user.ssn = '123-45-6789'
56
+ user.ssn # returns the unencrypted object ie. '123-45-6789'
57
+ user.encrypted_ssn # returns the encrypted version of :ssn
58
+ user.save
59
+
60
+ user = User.load
61
+ user.ssn # decrypts :encrypted_ssn and returns '123-45-6789'
62
+ ```
63
+
64
+ ### Encrypt/decrypt attribute class methods
65
+
66
+ Two class methods are available for each attribute: `User.encrypt_email` and `User.decrypt_email`. They accept as arguments the same options that the `attr_encrypted` class method accepts. For example:
67
+
68
+ ```ruby
69
+ key = SecureRandom.random_bytes(32)
70
+ iv = SecureRandom.random_bytes(12)
71
+ encrypted_email = User.encrypt_email('test@test.com', iv: iv, key: key)
72
+ email = User.decrypt_email(encrypted_email, iv: iv, key: key)
73
+ ```
74
+
75
+ The `attr_encrypted` class method is also aliased as `attr_encryptor` to conform to Ruby's `attr_` naming conventions. I should have called this project `attr_encryptor` but it was too late when I realized it ='(.
76
+
77
+ ### attr_encrypted with database persistence
78
+
79
+ By default, `attr_encrypted` uses the `:per_attribute_iv` encryption mode. This mode requires a column to store your cipher text and a column to store your IV (initialization vector).
80
+
81
+ Create or modify the table that your model uses to add a column with the `encrypted_` prefix (which can be modified, see below), e.g. `encrypted_ssn` via a migration like the following:
82
+
83
+ ```ruby
84
+ create_table :users do |t|
85
+ t.string :name
86
+ t.string :encrypted_ssn
87
+ t.string :encrypted_ssn_iv
88
+ t.timestamps
89
+ end
90
+ ```
91
+
92
+ You can use a string or binary column type. (See the encode option section below for more info)
93
+
94
+ If you use the same key for each record, add a unique index on the IV. Repeated IVs with AES-GCM (the default algorithm) allow an attacker to recover the key.
95
+
96
+ ```ruby
97
+ add_index :users, :encrypted_ssn_iv, unique: true
98
+ ```
99
+
100
+ ### Specifying the encrypted attribute name
101
+
102
+ By default, the encrypted attribute name is `encrypted_#{attribute}` (e.g. `attr_encrypted :email` would create an attribute named `encrypted_email`). So, if you're storing the encrypted attribute in the database, you need to make sure the `encrypted_#{attribute}` field exists in your table. You have a couple of options if you want to name your attribute or db column something else, see below for more details.
103
+
104
+
105
+ ## attr_encrypted options
106
+
107
+ #### Options are evaluated
108
+ All options will be evaluated at the instance level. If you pass in a symbol it will be passed as a message to the instance of your class. If you pass a proc or any object that responds to `:call` it will be called. You can pass in the instance of your class as an argument to the proc. Anything else will be returned. For example:
109
+
110
+ ##### Symbols representing instance methods
111
+
112
+ Here is an example class that uses an instance method to determines the encryption key to use.
113
+
114
+ ```ruby
115
+ class User
116
+ attr_encrypted :email, key: :encryption_key
117
+
118
+ def encryption_key
119
+ # does some fancy logic and returns an encryption key
120
+ end
121
+ end
122
+ ```
123
+
124
+
125
+ ##### Procs
126
+
127
+ Here is an example of passing a proc/lambda object as the `:key` option as well:
128
+
129
+ ```ruby
130
+ class User
131
+ attr_encrypted :email, key: proc { |user| user.key }
132
+ end
133
+ ```
134
+
135
+
136
+ ### Default options
137
+
138
+ The following are the default options used by `attr_encrypted`:
139
+
140
+ ```ruby
141
+ prefix: 'encrypted_',
142
+ suffix: '',
143
+ if: true,
144
+ unless: false,
145
+ encode: false,
146
+ encode_iv: true,
147
+ encode_salt: true,
148
+ default_encoding: 'm',
149
+ marshal: false,
150
+ marshaler: Marshal,
151
+ dump_method: 'dump',
152
+ load_method: 'load',
153
+ encryptor: Encryptor,
154
+ encrypt_method: 'encrypt',
155
+ decrypt_method: 'decrypt',
156
+ mode: :per_attribute_iv,
157
+ algorithm: 'aes-256-gcm',
158
+ allow_empty_value: false
159
+ ```
160
+
161
+ All of the aforementioned options are explained in depth below.
162
+
163
+ Additionally, you can specify default options for all encrypted attributes in your class. Instead of having to define your class like this:
164
+
165
+ ```ruby
166
+ class User
167
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!', prefix: '', suffix: '_crypted'
168
+ attr_encrypted :ssn, key: 'a different secret key', prefix: '', suffix: '_crypted'
169
+ attr_encrypted :credit_card, key: 'another secret key', prefix: '', suffix: '_crypted'
170
+ end
171
+ ```
172
+
173
+ You can simply define some default options like so:
174
+
175
+ ```ruby
176
+ class User
177
+ attr_encrypted_options.merge!(prefix: '', :suffix => '_crypted')
178
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!'
179
+ attr_encrypted :ssn, key: 'a different secret key'
180
+ attr_encrypted :credit_card, key: 'another secret key'
181
+ end
182
+ ```
183
+
184
+ This should help keep your classes clean and DRY.
185
+
186
+ ### The `:attribute` option
187
+
188
+ You can simply pass the name of the encrypted attribute as the `:attribute` option:
189
+
190
+ ```ruby
191
+ class User
192
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!', attribute: 'email_encrypted'
193
+ end
194
+ ```
195
+
196
+ This would generate an attribute named `email_encrypted`
197
+
198
+
199
+ ### The `:prefix` and `:suffix` options
200
+
201
+ If you don't like the `encrypted_#{attribute}` naming convention then you can specify your own:
202
+
203
+ ```ruby
204
+ class User
205
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!', prefix: 'secret_', suffix: '_crypted'
206
+ end
207
+ ```
208
+
209
+ This would generate the following attribute: `secret_email_crypted`.
210
+
211
+
212
+ ### The `:key` option
213
+
214
+ The `:key` option is used to pass in a data encryption key to be used with whatever encryption class you use. If you're using `Encryptor`, the key must meet minimum length requirements respective to the algorithm that you use; aes-256 requires a 256 bit key, etc. The `:key` option is not required (see custom encryptor below).
215
+
216
+
217
+ ##### Unique keys for each attribute
218
+
219
+ You can specify unique keys for each attribute if you'd like:
220
+
221
+ ```ruby
222
+ class User
223
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!'
224
+ attr_encrypted :ssn, key: 'a different secret key'
225
+ end
226
+ ```
227
+
228
+ It is recommended to use a symbol or a proc for the key and to store information regarding what key was used to encrypt your data. (See below for more details.)
229
+
230
+
231
+ ### The `:if` and `:unless` options
232
+
233
+ There may be times that you want to only encrypt when certain conditions are met. For example maybe you're using rails and you don't want to encrypt attributes when you're in development mode. You can specify conditions like this:
234
+
235
+ ```ruby
236
+ class User < ActiveRecord::Base
237
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!', unless: Rails.env.development?
238
+ attr_encrypted :ssn, key: 'This is a key that is 256 bits!!', if: Rails.env.development?
239
+ end
240
+ ```
241
+
242
+ You can specify both `:if` and `:unless` options.
243
+
244
+
245
+ ### The `:encryptor`, `:encrypt_method`, and `:decrypt_method` options
246
+
247
+ The `Encryptor` class is used by default. You may use your own custom encryptor by specifying the `:encryptor`, `:encrypt_method`, and `:decrypt_method` options.
248
+
249
+ Lets suppose you'd like to use this custom encryptor class:
250
+
251
+ ```ruby
252
+ class SillyEncryptor
253
+ def self.silly_encrypt(options)
254
+ (options[:value] + options[:secret_key]).reverse
255
+ end
256
+
257
+ def self.silly_decrypt(options)
258
+ options[:value].reverse.gsub(/#{options[:secret_key]}$/, '')
259
+ end
260
+ end
261
+ ```
262
+
263
+ Simply set up your class like so:
264
+
265
+ ```ruby
266
+ class User
267
+ attr_encrypted :email, secret_key: 'This is a key that is 256 bits!!', encryptor: SillyEncryptor, encrypt_method: :silly_encrypt, decrypt_method: :silly_decrypt
268
+ end
269
+ ```
270
+
271
+ Any options that you pass to `attr_encrypted` will be passed to the encryptor class along with the `:value` option which contains the string to encrypt/decrypt. Notice that the above example uses `:secret_key` instead of `:key`. See [encryptor](https://github.com/attr-encrypted/encryptor) for more info regarding the default encryptor class.
272
+
273
+
274
+ ### The `:mode` option
275
+
276
+ The mode options allows you to specify in what mode your data will be encrypted. There are currently three modes: `:per_attribute_iv`, `:per_attribute_iv_and_salt`, and `:single_iv_and_salt`.
277
+
278
+ __NOTE: `:per_attribute_iv_and_salt` and `:single_iv_and_salt` modes are deprecated and will be removed in the next major release.__
279
+
280
+
281
+ ### The `:algorithm` option
282
+
283
+ The default `Encryptor` class uses the standard ruby OpenSSL library. Its default algorithm is `aes-256-gcm`. You can modify this by passing the `:algorithm` option to the `attr_encrypted` call like so:
284
+
285
+ ```ruby
286
+ class User
287
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!', algorithm: 'aes-256-cbc'
288
+ end
289
+ ```
290
+
291
+ To view a list of all cipher algorithms that are supported on your platform, run the following code in your favorite Ruby REPL:
292
+
293
+ ```ruby
294
+ require 'openssl'
295
+ puts OpenSSL::Cipher.ciphers
296
+ ```
297
+ See [Encryptor](https://github.com/attr-encrypted/encryptor#algorithms) for more information.
298
+
299
+
300
+ ### The `:encode`, `:encode_iv`, `:encode_salt`, and `:default_encoding` options
301
+
302
+ You're probably going to be storing your encrypted attributes somehow (e.g. filesystem, database, etc). You can simply pass the `:encode` option to automatically encode/decode when encrypting/decrypting. The default behavior assumes that you're using a string column type and will base64 encode your cipher text. If you choose to use the binary column type then encoding is not required, but be sure to pass in `false` with the `:encode` option.
303
+
304
+ ```ruby
305
+ class User
306
+ attr_encrypted :email, key: 'some secret key', encode: true, encode_iv: true, encode_salt: true
307
+ end
308
+ ```
309
+
310
+ The default encoding is `m` (base64). You can change this by setting `encode: 'some encoding'`. See [`Array#pack`](http://ruby-doc.org/core-2.3.0/Array.html#method-i-pack) for more encoding options.
311
+
312
+
313
+ ### The `:marshal`, `:dump_method`, and `:load_method` options
314
+
315
+ You may want to encrypt objects other than strings (e.g. hashes, arrays, etc). If this is the case, simply pass the `:marshal` option to automatically marshal when encrypting/decrypting.
316
+
317
+ ```ruby
318
+ class User
319
+ attr_encrypted :credentials, key: 'some secret key', marshal: true
320
+ end
321
+ ```
322
+
323
+ You may also optionally specify `:marshaler`, `:dump_method`, and `:load_method` if you want to use something other than the default `Marshal` object.
324
+
325
+ ### The `:allow_empty_value` option
326
+
327
+ You may want to encrypt empty strings or nil so as to not reveal which records are populated and which records are not.
328
+
329
+ ```ruby
330
+ class User
331
+ attr_encrypted :credentials, key: 'some secret key', marshal: true, allow_empty_value: true
332
+ end
333
+ ```
334
+
335
+
336
+ ## ORMs
337
+
338
+ ### ActiveRecord
339
+
340
+ If you're using this gem with `ActiveRecord`, you get a few extra features:
341
+
342
+ #### Default options
343
+
344
+ The `:encode` option is set to true by default.
345
+
346
+ #### Dynamic `find_by_` and `scoped_by_` methods
347
+
348
+ Let's say you'd like to encrypt your user's email addresses, but you also need a way for them to login. Simply set up your class like so:
349
+
350
+ ```ruby
351
+ class User < ActiveRecord::Base
352
+ attr_encrypted :email, key: 'This is a key that is 256 bits!!'
353
+ attr_encrypted :password, key: 'some other secret key'
354
+ end
355
+ ```
356
+
357
+ You can now lookup and login users like so:
358
+
359
+ ```ruby
360
+ User.find_by_email_and_password('test@example.com', 'testing')
361
+ ```
362
+
363
+ The call to `find_by_email_and_password` is intercepted and modified to `find_by_encrypted_email_and_encrypted_password('ENCRYPTED EMAIL', 'ENCRYPTED PASSWORD')`. The dynamic scope methods like `scoped_by_email_and_password` work the same way.
364
+
365
+ NOTE: This only works if all records are encrypted with the same encryption key (per attribute).
366
+
367
+ __NOTE: This feature is deprecated and will be removed in the next major release.__
368
+
369
+
370
+ ### DataMapper and Sequel
371
+
372
+ #### Default options
373
+
374
+ The `:encode` option is set to true by default.
375
+
376
+
377
+ ## Deprecations
378
+
379
+ attr_encrypted v2.0.0 now depends on encryptor v2.0.0. As part of both major releases many insecure defaults and behaviors have been deprecated. The new default behavior is as follows:
380
+
381
+ * Default `:mode` is now `:per_attribute_iv`, the default `:mode` in attr_encrypted v1.x was `:single_iv_and_salt`.
382
+ * Default `:algorithm` is now 'aes-256-gcm', the default `:algorithm` in attr_encrypted v1.x was 'aes-256-cbc'.
383
+ * The encryption key provided must be of appropriate length respective to the algorithm used. Previously, encryptor did not verify minimum key length.
384
+ * The dynamic finders available in ActiveRecord will only work with `:single_iv_and_salt` mode. It is strongly advised that you do not use this mode. If you can search the encrypted data, it wasn't encrypted securely. This functionality will be deprecated in the next major release.
385
+ * `:per_attribute_iv_and_salt` and `:single_iv_and_salt` modes are deprecated and will be removed in the next major release.
386
+
387
+ Backwards compatibility is supported by providing a special option that is passed to encryptor, namely, `:insecure_mode`:
388
+
389
+ ```ruby
390
+ class User
391
+ attr_encrypted :email, key: 'a secret key', algorithm: 'aes-256-cbc', mode: :single_iv_and_salt, insecure_mode: true
392
+ end
393
+ ```
394
+
395
+ The `:insecure_mode` option will allow encryptor to ignore the new security requirements. It is strongly advised that if you use this older insecure behavior that you migrate to the newer more secure behavior.
396
+
397
+
398
+ ## Upgrading from attr_encrypted v1.x to v3.x
399
+
400
+ Modify your gemfile to include the new version of attr_encrypted:
401
+
402
+ ```ruby
403
+ gem attr_encrypted, "~> 3.0.0"
404
+ ```
405
+
406
+ The update attr_encrypted:
407
+
408
+ ```bash
409
+ bundle update attr_encrypted
410
+ ```
411
+
412
+ Then modify your models using attr\_encrypted to account for the changes in default options. Specifically, pass in the `:mode` and `:algorithm` options that you were using if you had not previously done so. If your key is insufficient length relative to the algorithm that you use, you should also pass in `insecure_mode: true`; this will prevent Encryptor from raising an exception regarding insufficient key length. Please see the Deprecations sections for more details including an example of how to specify your model with default options from attr_encrypted v1.x.
413
+
414
+ ## Upgrading from attr_encrypted v2.x to v3.x
415
+
416
+ A bug was discovered in Encryptor v2.0.0 that inccorectly set the IV when using an AES-\*-GCM algorithm. Unfornately fixing this major security issue results in the inability to decrypt records encrypted using an AES-*-GCM algorithm from Encryptor v2.0.0. Please see [Upgrading to Encryptor v3.0.0](https://github.com/attr-encrypted/encryptor#upgrading-from-v200-to-v300) for more info.
417
+
418
+ It is strongly advised that you re-encrypt your data encrypted with Encryptor v2.0.0. However, you'll have to take special care to re-encrypt. To decrypt data encrypted with Encryptor v2.0.0 using an AES-\*-GCM algorithm you can use the `:v2_gcm_iv` option.
419
+
420
+ It is recommended that you implement a strategy to insure that you do not mix the encryption implementations of Encryptor. One way to do this is to re-encrypt everything while your application is offline.Another way is to add a column that keeps track of what implementation was used. The path that you choose will depend on your situtation. Below is an example of how you might go about re-encrypting your data.
421
+
422
+ ```ruby
423
+ class User
424
+ attr_encrypted :ssn, key: :encryption_key, v2_gcm_iv: is_decrypting?(:ssn)
425
+
426
+ def is_decrypting?(attribute)
427
+ encrypted_attributes[attribute][:operation] == :decrypting
428
+ end
429
+ end
430
+
431
+ User.all.each do |user|
432
+ old_ssn = user.ssn
433
+ user.ssn= old_ssn
434
+ user.save
435
+ end
436
+ ```
437
+
438
+ ## Things to consider before using attr_encrypted
439
+
440
+ #### Searching, joining, etc
441
+ While choosing to encrypt at the attribute level is the most secure solution, it is not without drawbacks. Namely, you cannot search the encrypted data, and because you can't search it, you can't index it either. You also can't use joins on the encrypted data. Data that is securely encrypted is effectively noise. So any operations that rely on the data not being noise will not work. If you need to do any of the aforementioned operations, please consider using database and file system encryption along with transport encryption as it moves through your stack.
442
+
443
+ #### Data leaks
444
+ Please also consider where your data leaks. If you're using attr_encrypted with Rails, it's highly likely that this data will enter your app as a request parameter. You'll want to be sure that you're filtering your request params from you logs or else your data is sitting in the clear in your logs. [Parameter Filtering in Rails](http://apidock.com/rails/ActionDispatch/Http/FilterParameters) Please also consider other possible leak points.
445
+
446
+ #### Storage requirements
447
+ When storing your encrypted data, please consider the length requirements of the db column that you're storing the cipher text in. Older versions of Mysql attempt to 'help' you by truncating strings that are too large for the column. When this happens, you will not be able to decrypt your data. [MySQL Strict Trans](http://www.davidpashley.com/2009/02/15/silently-truncated/)
448
+
449
+ #### Metadata regarding your crypto implementation
450
+ It is advisable to also store metadata regarding the circumstances of your encrypted data. Namely, you should store information about the key used to encrypt your data, as well as the algorithm. Having this metadata with every record will make key rotation and migrating to a new algorithm signficantly easier. It will allow you to continue to decrypt old data using the information provided in the metadata and new data can be encrypted using your new key and algorithm of choice.
451
+
452
+ #### Enforcing the IV as a nonce
453
+ On a related note, most algorithms require that your IV be unique for every record and key combination. You can enforce this using composite unique indexes on your IV and encryption key name/id column. [RFC 5084](https://tools.ietf.org/html/rfc5084#section-1.5)
454
+
455
+ #### Unique key per record
456
+ Lastly, while the `:per_attribute_iv_and_salt` mode is more secure than `:per_attribute_iv` mode because it uses a unique key per record, it uses a PBKDF function which introduces a huge performance hit (175x slower by my benchmarks). There are other ways of deriving a unique key per record that would be much faster.
457
+
458
+ ## Note on Patches/Pull Requests
459
+
460
+ * Fork the project.
461
+ * Make your feature addition or bug fix.
462
+ * Add tests for it. This is important so I don't break it in a
463
+ future version unintentionally.
464
+ * Commit, do not mess with rakefile, version, changelog, or history.
465
+ * Send me a pull request. Bonus points for topic branches.