hash_redactor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: afc446801a4d9742e40d28954ed609b4cd58aad3
4
+ data.tar.gz: c959b0bd8e2c8c4193bb2526423ab46ba4495982
5
+ SHA512:
6
+ metadata.gz: 74b7d907b37f7855c16a45aecc4bd4b6102a2ffb9a2516765a623fc3b7339c805c14b6baa73f20560890c64efaa86ccf9dd6520221c52c14f917c800dfad51ba
7
+ data.tar.gz: 14cefa25e6f0ed169952e35910d5080ce519d49d5a12467a04350773d297926e16c6c9573e4ab6532444d16b2371002bfd36c8b7e6482b55947e5871b97d95f1
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - 2.3.0
8
+ - rbx
9
+ matrix:
10
+ exclude:
11
+ allow_failures:
12
+ - rvm: rbx
13
+ fast_finish: true
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hash_redactor.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Chris Jensen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # HashRedactor
2
+
3
+ Used to redact a hash by removing, digesting or encrypting keys.
4
+
5
+ Makes use of [`attr_encrypted`](https://github.com/attr-encrypted/attr_encrypted) to perform encryption.
6
+
7
+ Useful if you have large JSON objects or similar that are coming in through API calls or elsewhere and you want to scrub the private data before storing it to the database.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'hash_redactor'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install hash_redactor
24
+
25
+ ## Usage
26
+
27
+ Initialize the HashRedactor with a set of fields that you want to redact in hashes that get passed to it.
28
+
29
+ You can choose 3 ways to redact each field:
30
+
31
+ + `:remove` - The field is simply deleted
32
+ + `:digest` - The field is passed through a one way hash function (SHA256)
33
+ + `:encrypt` - The field is encrypted
34
+
35
+ If you plan on using digest or encrypt, then you will need to specify `:digest_salt` or `:encryption_key` respectively in the options.
36
+
37
+ ```ruby
38
+ fields_to_redact = {
39
+ :ssn => :remove,
40
+ :email => :digest,
41
+ :medical_history => :encrypt
42
+ }
43
+
44
+ redactor = HashRedactor::HashRedactor.new({
45
+ redact: fields_to_redact,
46
+ encryption_key: 'a really secure key no one will ever guess it',
47
+ digest_salt: 'a secret salt'
48
+ })
49
+ ```
50
+
51
+ Once initialized, you can simply call `#redact` on hashes of data.
52
+ If a key you have specified to redact is missing, HashRedactor will silently ignore it's absence.
53
+ Any key's not specified in your redact hash will be left unchanged.
54
+
55
+
56
+ ```ruby
57
+ raw_data = {
58
+ ssn: 'a very personal number',
59
+ email: 'personal@email.com',
60
+ medical_history: 'Some very private information'
61
+ plaintext: 'This will never be changed'
62
+ }
63
+
64
+ safe_data = redactor.redact(raw_data)
65
+
66
+ safe_data[:ssn] # nil
67
+ safe_data[:email_digest] # One way hash of the email
68
+ safe_data[:encrypted_medical_history] # Encrypted medical history
69
+ safe_data[:plaintext] # This will never be changed
70
+ ```
71
+
72
+ To retrieve your encrypted data, pass it through decrypt.
73
+
74
+ ```ruby
75
+ loaded_data = redactor.decrypt(safe_data)
76
+ loaded_data[:medical_history] # 'Some very private information'
77
+ ```
78
+
79
+ ## Digest method
80
+
81
+ Digest may be used in cases where you don't want to keep the data due to privacy concerns, but want to compare it.
82
+ For example, maybe you have a large database of people whom you never need to contact by email, but you want to use the email address to check for duplicates.
83
+
84
+ To enhance protection of the data you should provide a salt in the configuration options and you should not store that salt in your database.
85
+
86
+ Once the field has been digested, it's key will have _digest appended.
87
+
88
+ ## Encrypt method
89
+
90
+ Encryption is for fields that you do need to recover, but don't want to store in plaintext in the database.
91
+
92
+ [`attr_encrypted`](https://github.com/attr-encrypted/attr_encrypted) is used to perform encryption.
93
+
94
+ Once the field has been encrypted, the original field is removed from the hash, and replaced with `:encrypted_FIELDNAME` and `:encrypted_FIELDNAME_iv` - both these fields must be present in the hash for decrypt to succeed.
95
+
96
+ For example, if the field you want to encrypt has the key `:email`, then the redacred hash will have the keys `:encrypted_email` and `:encrypted_email_iv`.
97
+
98
+ ## Redacted Hash
99
+
100
+ From the example above, after running #redact, the safe_data hash would look something like this:
101
+
102
+ ```ruby
103
+ {
104
+ :email_digest=>"VXJsQeG81HYWOb30XfpBdbqEFH1f4VaFLTqHCdxCmj8=", :encrypted_medical_history=>"2JIN3Yhxvm/m7qlE+n4pMT9yckXuPa+2IlMBFQMcbP1pcwyrG7wy0TP4scgx\n",
105
+ :encrypted_medical_history_iv=>"tqMQa1aYTdJuMhrD\n",
106
+ :plaintext => 'This will never be changed'
107
+ }
108
+ ```
109
+
110
+ ## Options
111
+
112
+ Default options are:
113
+ ```ruby
114
+ digest_salt: "",
115
+ encryption_key: nil,
116
+ encode: true,
117
+ encode_iv: true,
118
+ default_encoding: 'm'
119
+ ```
120
+
121
+ ### :digest_salt
122
+
123
+ Salt to use when applying digest method
124
+
125
+ ### :encryption_key
126
+
127
+ Key to use for encryption and decryption of values
128
+
129
+ ### :encode, :encode_iv, :default_encoding
130
+
131
+ Determines how (if at all) to encode the encrypted data and it's iv
132
+
133
+ The default encoding is m (base64). You can change this by setting encode: `'some encoding'`. See [Arrary#pack](http://ruby-doc.org/core-2.3.0/Array.html#method-i-pack) for more encoding options.
134
+
135
+ ## Development
136
+
137
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
138
+
139
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
140
+
141
+ ## Contributing
142
+
143
+ 1. Fork it ( https://github.com/chrisjensen/hash_redactor/fork )
144
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
145
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
146
+ 4. Push to the branch (`git push origin my-new-feature`)
147
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require File.join('bundler', 'gem_tasks')
3
+ require File.join('rspec', 'core', 'rake_task')
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hash_redactor"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hash_redactor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hash_redactor"
8
+ spec.version = HashRedactor::VERSION
9
+ spec.authors = ["Chris Jensen"]
10
+ spec.email = ["chris@broadthought.co"]
11
+
12
+ spec.summary = %q{Redact specified values in a hash}
13
+ spec.description = %q{Removes, digests or encrypts selected values in a ruby hash}
14
+ spec.homepage = "https://github.com/chrisjensen/hash_redactor"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency 'attr_encrypted', '~> 3.0.0'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.8"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency 'rspec', '~> 2.14'
26
+ end
@@ -0,0 +1,117 @@
1
+ require 'attr_encrypted'
2
+
3
+ module HashRedactor
4
+ class HashRedactor
5
+ def initialize(opts = {})
6
+ @options = default_options.merge opts
7
+
8
+ @options[:encode] = @options[:default_encoding] if @options[:encode] == true
9
+ @options[:encode_iv] = @options[:default_encoding] if @options[:encode_iv] == true
10
+ end
11
+
12
+ def default_options
13
+ {
14
+ digest_salt: "",
15
+ encryption_key: nil,
16
+ encode: true,
17
+ encode_iv: true,
18
+ default_encoding: 'm'
19
+ }
20
+ end
21
+
22
+ # Removes, digests or encrypts fields in a hash
23
+ # NOTE: This should NOT be used to protect password fields or similar
24
+ # The purpose of hashing is to reduce data to a form that can be *quickly*
25
+ # compared against other records without revealing the original value.
26
+ # To allow for this, all hashes are created using the *same salt* which is
27
+ # not secure enough for password protection
28
+ # For passwords, use BCrypt
29
+ def redact(data, opts = {})
30
+ options = @options.merge(opts)
31
+
32
+ redact_hash = options[:redact]
33
+
34
+ raise "Don't know what to redact. Please configure the redact hash when initializing or pass as an argument to redact." unless redact_hash && redact_hash.any?
35
+
36
+ result = data.clone
37
+
38
+ redact_hash.each do |hash_key,how|
39
+ if data.has_key? hash_key
40
+ case how
41
+ when :remove
42
+ nil
43
+ when :digest
44
+ result_key = (hash_key.to_s + '_digest').to_sym
45
+
46
+ result[result_key] = Digest::SHA256.base64digest(
47
+ result[hash_key] + options[:digest_salt])
48
+ when :encrypt
49
+ raise "No encryption key specified. Please pass :encryption_key in options to new or redact" unless options[:encryption_key]
50
+
51
+ crypt_key = options[:encryption_key]
52
+ iv = SecureRandom.random_bytes(12)
53
+ data_key = ('encrypted_' + hash_key.to_s).to_sym
54
+ iv_key = ('encrypted_' + hash_key.to_s + '_iv').to_sym
55
+
56
+ encrypted_value = EncryptorInterface.encrypt(:data, result[hash_key], iv: iv, key: crypt_key)
57
+
58
+ encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
59
+ iv = [iv].pack(options[:encode_iv]) if options[:encode_iv]
60
+
61
+ result[data_key] = encrypted_value
62
+ result[iv_key] = iv
63
+ else
64
+ raise "redact called with unknown operation on #{hash_key.to_s}: #{how.to_s}"
65
+ end
66
+
67
+ result.delete hash_key
68
+ end
69
+ end
70
+
71
+ result
72
+ end
73
+
74
+ def decrypt(data, opts = {})
75
+ options = @options.merge opts
76
+
77
+ redact_hash = options[:redact]
78
+
79
+ raise "Don't know what to decrypt. Please configure the redact hash when initializing or pass as an argument to #decrypt." unless redact_hash && redact_hash.any?
80
+
81
+ raise "No encryption key specified. Please pass :encryption_key in options to new or decrypt" unless options[:encryption_key]
82
+
83
+ result = data.clone
84
+
85
+ redact_hash.each do |hash_key,how|
86
+ if (how == :encrypt)
87
+ data_key = ('encrypted_' + hash_key.to_s).to_sym
88
+ iv_key = ('encrypted_' + hash_key.to_s + '_iv').to_sym
89
+
90
+ if (data.has_key? data_key)
91
+ iv = result[iv_key]
92
+ crypt_key = options[:encryption_key]
93
+
94
+ encrypted_value = result[data_key]
95
+
96
+ # Decode if necessary
97
+ iv = iv.unpack(options[:encode_iv]).first if options[:encode_iv]
98
+ encrypted_value = encrypted_value.unpack(options[:encode]).first if options[:encode]
99
+
100
+ decrypted_value = EncryptorInterface.decrypt(:data, encrypted_value,
101
+ iv: iv, key: crypt_key)
102
+
103
+ result[hash_key] = decrypted_value
104
+ end
105
+ end
106
+ end
107
+
108
+ result
109
+ end
110
+ end
111
+
112
+ class EncryptorInterface
113
+ extend AttrEncrypted
114
+
115
+ attr_encrypted :data
116
+ end
117
+ end
@@ -0,0 +1,3 @@
1
+ module HashRedactor
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,2 @@
1
+ require File.join('hash_redactor', 'hash_redactor')
2
+
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hash_redactor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Jensen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: attr_encrypted
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.14'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.14'
69
+ description: Removes, digests or encrypts selected values in a ruby hash
70
+ email:
71
+ - chris@broadthought.co
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - .travis.yml
79
+ - CODE_OF_CONDUCT.md
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/setup
86
+ - hash_redactor.gemspec
87
+ - lib/hash_redactor.rb
88
+ - lib/hash_redactor/hash_redactor.rb
89
+ - lib/hash_redactor/version.rb
90
+ homepage: https://github.com/chrisjensen/hash_redactor
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.4.6
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Redact specified values in a hash
114
+ test_files: []