crypt_keeper 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/crypt_keeper.rb +1 -0
- data/lib/crypt_keeper/helper.rb +31 -3
- data/lib/crypt_keeper/log_subscriber/mysql_aes.rb +0 -2
- data/lib/crypt_keeper/log_subscriber/postgres_pgp.rb +1 -4
- data/lib/crypt_keeper/provider/postgres_base.rb +28 -0
- data/lib/crypt_keeper/provider/postgres_pgp.rb +7 -8
- data/lib/crypt_keeper/provider/postgres_pgp_public_key.rb +4 -20
- data/lib/crypt_keeper/version.rb +1 -1
- data/spec/crypt_keeper/log_subscriber/mysql_aes_spec.rb +0 -6
- data/spec/crypt_keeper/log_subscriber/postgres_pgp_spec.rb +30 -46
- data/spec/crypt_keeper/provider/postgres_pgp_public_key_spec.rb +1 -0
- data/spec/crypt_keeper/provider/postgres_pgp_spec.rb +1 -0
- data/spec/support/logging.rb +2 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78d7a7f1ea679f9a1542a65b537a446065c9cd27
|
4
|
+
data.tar.gz: 518d78e7d4a80e07faa80b9eac9c3b268e2db2c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30b9ccbea99c9ac89ee4bf4c9df73fc0b95e2ae3f65aca3e9936a95bd406d271e672e73cefa2c717a2f0c057c638ca876a238c7058da2c3ad6e3f2d2d12720cb
|
7
|
+
data.tar.gz: 07f33f2caca79c5be2c45f2365601e8939b5c64200fba53c00de3e70a9d6b221a68efe47aad00fc7b42003bce2c964ce68701656966149afe4b65dfc45a4e0a7
|
data/lib/crypt_keeper.rb
CHANGED
@@ -6,6 +6,7 @@ require 'crypt_keeper/helper'
|
|
6
6
|
require 'crypt_keeper/provider/base'
|
7
7
|
require 'crypt_keeper/provider/aes_new'
|
8
8
|
require 'crypt_keeper/provider/mysql_aes_new'
|
9
|
+
require 'crypt_keeper/provider/postgres_base'
|
9
10
|
require 'crypt_keeper/provider/postgres_pgp'
|
10
11
|
require 'crypt_keeper/provider/postgres_pgp_public_key'
|
11
12
|
|
data/lib/crypt_keeper/helper.rb
CHANGED
@@ -3,10 +3,38 @@ module CryptKeeper
|
|
3
3
|
module SQL
|
4
4
|
private
|
5
5
|
|
6
|
-
# Private: Sanitize an sql query and then execute it
|
7
|
-
|
6
|
+
# Private: Sanitize an sql query and then execute it.
|
7
|
+
#
|
8
|
+
# query - the sql query
|
9
|
+
# new_transaction - if the query should run inside a new transaction
|
10
|
+
#
|
11
|
+
# Returns the ActiveRecord response.
|
12
|
+
def escape_and_execute_sql(query, new_transaction: false)
|
8
13
|
query = ::ActiveRecord::Base.send :sanitize_sql_array, query
|
9
|
-
|
14
|
+
|
15
|
+
if CryptKeeper.silence_logs?
|
16
|
+
::ActiveRecord::Base.logger.silence do
|
17
|
+
execute_sql(query, new_transaction: new_transaction)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
execute_sql(query, new_transaction: new_transaction)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Private: Executes the query.
|
25
|
+
#
|
26
|
+
# query - the sql query
|
27
|
+
# new_transaction - if the query should run inside a new transaction
|
28
|
+
#
|
29
|
+
# Returns an Array.
|
30
|
+
def execute_sql(query, new_transaction: false)
|
31
|
+
if new_transaction
|
32
|
+
::ActiveRecord::Base.transaction(requires_new: true) do
|
33
|
+
::ActiveRecord::Base.connection.execute(query).first
|
34
|
+
end
|
35
|
+
else
|
36
|
+
::ActiveRecord::Base.connection.execute(query).first
|
37
|
+
end
|
10
38
|
end
|
11
39
|
end
|
12
40
|
|
@@ -14,8 +14,6 @@ module CryptKeeper
|
|
14
14
|
payload = event.payload[:sql]
|
15
15
|
.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
16
16
|
|
17
|
-
return if CryptKeeper.silence_logs? && payload =~ filter
|
18
|
-
|
19
17
|
event.payload[:sql] = payload.gsub(filter) do |_|
|
20
18
|
"#{$1}([FILTERED])"
|
21
19
|
end
|
@@ -4,7 +4,7 @@ require 'active_support/lazy_load_hooks'
|
|
4
4
|
module CryptKeeper
|
5
5
|
module LogSubscriber
|
6
6
|
module PostgresPgp
|
7
|
-
FILTER = /(\(*)
|
7
|
+
FILTER = /(\(*)(?<operation>pgp_sym_encrypt|pgp_sym_decrypt|pgp_pub_encrypt|pgp_pub_decrypt|pgp_key_id)(\(+.*\)+)/im
|
8
8
|
|
9
9
|
# Public: Prevents sensitive data from being logged
|
10
10
|
#
|
@@ -13,9 +13,6 @@ module CryptKeeper
|
|
13
13
|
# Returns a boolean.
|
14
14
|
def sql(event)
|
15
15
|
payload = crypt_keeper_payload_parse(event.payload[:sql])
|
16
|
-
|
17
|
-
return if CryptKeeper.silence_logs? && payload =~ FILTER
|
18
|
-
|
19
16
|
event.payload[:sql] = crypt_keeper_filter_postgres_log(payload)
|
20
17
|
super(event)
|
21
18
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'crypt_keeper/log_subscriber/postgres_pgp'
|
2
|
+
|
3
|
+
module CryptKeeper
|
4
|
+
module Provider
|
5
|
+
class PostgresBase < Base
|
6
|
+
include CryptKeeper::Helper::SQL
|
7
|
+
include CryptKeeper::LogSubscriber::PostgresPgp
|
8
|
+
|
9
|
+
INVALID_DATA_ERROR = "Wrong key or corrupt data".freeze
|
10
|
+
|
11
|
+
# Public: Checks if value is already encrypted.
|
12
|
+
#
|
13
|
+
# Returns boolean
|
14
|
+
def encrypted?(value)
|
15
|
+
begin
|
16
|
+
escape_and_execute_sql(["SELECT pgp_key_id(?)", value.to_s],
|
17
|
+
new_transaction: true)['pgp_key_id'].present?
|
18
|
+
rescue ActiveRecord::StatementInvalid => e
|
19
|
+
if e.message.include?(INVALID_DATA_ERROR)
|
20
|
+
false
|
21
|
+
else
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,11 +1,6 @@
|
|
1
|
-
require 'crypt_keeper/log_subscriber/postgres_pgp'
|
2
|
-
|
3
1
|
module CryptKeeper
|
4
2
|
module Provider
|
5
|
-
class PostgresPgp <
|
6
|
-
include CryptKeeper::Helper::SQL
|
7
|
-
include CryptKeeper::LogSubscriber::PostgresPgp
|
8
|
-
|
3
|
+
class PostgresPgp < PostgresBase
|
9
4
|
attr_accessor :key
|
10
5
|
attr_accessor :pgcrypto_options
|
11
6
|
|
@@ -37,8 +32,12 @@ module CryptKeeper
|
|
37
32
|
# Returns a plaintext string
|
38
33
|
def decrypt(value)
|
39
34
|
rescue_invalid_statement do
|
40
|
-
|
41
|
-
|
35
|
+
if encrypted?(value)
|
36
|
+
escape_and_execute_sql(["SELECT pgp_sym_decrypt(?, ?)",
|
37
|
+
value, key])['pgp_sym_decrypt']
|
38
|
+
else
|
39
|
+
value
|
40
|
+
end
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
@@ -1,10 +1,6 @@
|
|
1
|
-
require 'crypt_keeper/log_subscriber/postgres_pgp'
|
2
|
-
|
3
1
|
module CryptKeeper
|
4
2
|
module Provider
|
5
|
-
class PostgresPgpPublicKey <
|
6
|
-
include CryptKeeper::Helper::SQL
|
7
|
-
|
3
|
+
class PostgresPgpPublicKey < PostgresBase
|
8
4
|
attr_accessor :key
|
9
5
|
|
10
6
|
def initialize(options = {})
|
@@ -33,25 +29,13 @@ module CryptKeeper
|
|
33
29
|
#
|
34
30
|
# Returns a plaintext string
|
35
31
|
def decrypt(value)
|
36
|
-
if @private_key.present?
|
37
|
-
escape_and_execute_sql(["SELECT pgp_pub_decrypt(?, dearmor(?), ?)",
|
32
|
+
if @private_key.present? && encrypted?(value)
|
33
|
+
escape_and_execute_sql(["SELECT pgp_pub_decrypt(?, dearmor(?), ?)",
|
34
|
+
value, @private_key, @key])['pgp_pub_decrypt']
|
38
35
|
else
|
39
36
|
value
|
40
37
|
end
|
41
38
|
end
|
42
|
-
|
43
|
-
# Public: Attempts to extract a PGP key id. If it's successful, it returns true
|
44
|
-
#
|
45
|
-
# Returns boolean
|
46
|
-
def encrypted?(value)
|
47
|
-
begin
|
48
|
-
ActiveRecord::Base.transaction(requires_new: true) do
|
49
|
-
escape_and_execute_sql(["SELECT pgp_key_id(?)", value.to_s])['pgp_key_id'].present?
|
50
|
-
end
|
51
|
-
rescue ActiveRecord::StatementInvalid
|
52
|
-
false
|
53
|
-
end
|
54
|
-
end
|
55
39
|
end
|
56
40
|
end
|
57
41
|
end
|
data/lib/crypt_keeper/version.rb
CHANGED
@@ -46,11 +46,5 @@ describe CryptKeeper::LogSubscriber::MysqlAes do
|
|
46
46
|
|
47
47
|
should_log_scrubbed_query(input: input_query, output: output_query)
|
48
48
|
end
|
49
|
-
|
50
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
51
|
-
CryptKeeper.silence_logs = true
|
52
|
-
|
53
|
-
should_not_log_query(input_query)
|
54
|
-
end
|
55
49
|
end
|
56
50
|
end
|
@@ -13,44 +13,32 @@ describe CryptKeeper::LogSubscriber::PostgresPgp do
|
|
13
13
|
CryptKeeper::Provider::PostgresPgp.new key: 'secret'
|
14
14
|
end
|
15
15
|
|
16
|
-
let(:
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
let(:input_search_query) do
|
25
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE ((pgp_sym_decrypt('f'), 'tool') = 'blah')) AND secret = 'testing'"
|
26
|
-
end
|
27
|
-
|
28
|
-
let(:output_search_query) do
|
29
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE decrypt([FILTERED]) AND secret = 'testing'"
|
30
|
-
end
|
16
|
+
let(:queries) {
|
17
|
+
{
|
18
|
+
"SELECT pgp_sym_encrypt('encrypt_value', 'encrypt_key') FROM DUAL;" => "SELECT pgp_sym_encrypt([FILTERED]) FROM DUAL;",
|
19
|
+
"SELECT pgp_sym_decrypt('encrypt_value') FROM DUAL;" => "SELECT pgp_sym_decrypt([FILTERED]) FROM DUAL;",
|
20
|
+
"SELECT pgp_key_id('encrypt_value') FROM DUAL;" => "SELECT pgp_key_id([FILTERED]) FROM DUAL;",
|
21
|
+
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE ((pgp_sym_decrypt('f'), 'tool') = 'blah')) AND secret = 'testing'" => "SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE pgp_sym_decrypt([FILTERED]) AND secret = 'testing'",
|
22
|
+
}
|
23
|
+
}
|
31
24
|
|
32
25
|
it "filters pgp functions" do
|
33
|
-
|
26
|
+
queries.each do |k, v|
|
27
|
+
should_log_scrubbed_query(input: k, output: v)
|
28
|
+
end
|
34
29
|
end
|
35
30
|
|
36
31
|
it "filters pgp functions in lowercase" do
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
it "filters pgp functions when searching" do
|
41
|
-
should_log_scrubbed_query(input: input_search_query, output: output_search_query)
|
32
|
+
queries.each do |k, v|
|
33
|
+
should_log_scrubbed_query(input: k.downcase, output: v.downcase.gsub(/filtered/, 'FILTERED'))
|
34
|
+
end
|
42
35
|
end
|
43
36
|
|
44
37
|
it "forces string encodings" do
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
51
|
-
CryptKeeper.silence_logs = true
|
52
|
-
|
53
|
-
should_not_log_query(input_query)
|
38
|
+
queries.each do |k, v|
|
39
|
+
k = "#{k}\255"
|
40
|
+
should_log_scrubbed_query(input: k, output: v)
|
41
|
+
end
|
54
42
|
end
|
55
43
|
end
|
56
44
|
|
@@ -68,27 +56,23 @@ describe CryptKeeper::LogSubscriber::PostgresPgp do
|
|
68
56
|
CryptKeeper::Provider::PostgresPgpPublicKey.new key: 'secret', public_key: public_key, private_key: private_key
|
69
57
|
end
|
70
58
|
|
71
|
-
let(:
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
"SELECT encrypt([FILTERED])"
|
78
|
-
end
|
59
|
+
let(:queries) {
|
60
|
+
{
|
61
|
+
"SELECT pgp_pub_encrypt('test', dearmor('#{public_key}')) FROM DUAL;" => "SELECT pgp_pub_encrypt([FILTERED]) FROM DUAL;",
|
62
|
+
"SELECT pgp_pub_decrypt('test', dearmor('#{public_key}'), '#{private_key}') FROM DUAL;" => "SELECT pgp_pub_decrypt([FILTERED]) FROM DUAL;",
|
63
|
+
}
|
64
|
+
}
|
79
65
|
|
80
66
|
it "filters pgp functions" do
|
81
|
-
|
67
|
+
queries.each do |k, v|
|
68
|
+
should_log_scrubbed_query(input: k, output: v)
|
69
|
+
end
|
82
70
|
end
|
83
71
|
|
84
72
|
it "filters pgp functions in lowercase" do
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
89
|
-
CryptKeeper.silence_logs = true
|
90
|
-
|
91
|
-
should_not_log_query(input_query)
|
73
|
+
queries.each do |k, v|
|
74
|
+
should_log_scrubbed_query(input: k.downcase, output: v.downcase.gsub(/filtered/, 'FILTERED'))
|
75
|
+
end
|
92
76
|
end
|
93
77
|
end
|
94
78
|
end
|
@@ -47,6 +47,7 @@ describe CryptKeeper::Provider::PostgresPgpPublicKey do
|
|
47
47
|
describe "#decrypt" do
|
48
48
|
specify { expect(subject.decrypt(cipher_text)).to eq(plain_text) }
|
49
49
|
specify { expect(subject.decrypt(integer_cipher_text)).to eq(integer_plain_text.to_s) }
|
50
|
+
specify { expect(subject.decrypt(plain_text)).to eq(plain_text) }
|
50
51
|
|
51
52
|
it "does not decrypt w/o private key" do
|
52
53
|
pgp = described_class.new key: ENCRYPTION_PASSWORD, public_key: public_key
|
@@ -44,6 +44,7 @@ describe CryptKeeper::Provider::PostgresPgp do
|
|
44
44
|
describe "#decrypt" do
|
45
45
|
specify { expect(subject.decrypt(cipher_text)).to eq(plain_text) }
|
46
46
|
specify { expect(subject.decrypt(integer_cipher_text)).to eq(integer_plain_text.to_s) }
|
47
|
+
specify { expect(subject.decrypt(plain_text)).to eq(plain_text) }
|
47
48
|
|
48
49
|
it "filters StatementInvalid errors" do
|
49
50
|
begin
|
data/spec/support/logging.rb
CHANGED
@@ -47,11 +47,8 @@ module CryptKeeper
|
|
47
47
|
def should_log_scrubbed_query(input:, output:)
|
48
48
|
queries = sql(input)
|
49
49
|
|
50
|
-
|
51
|
-
expect(
|
52
|
-
|
53
|
-
valid_output = queries.any? { |line| line.include? output }
|
54
|
-
expect(valid_output).to eq(true), "output query was not logged!"
|
50
|
+
expect(queries.join("\n")).to_not include(input)
|
51
|
+
expect(queries.join("\n")).to include(output)
|
55
52
|
end
|
56
53
|
|
57
54
|
# Public: Verifies that the given input query was not logged.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crypt_keeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Mazzi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -245,6 +245,7 @@ files:
|
|
245
245
|
- lib/crypt_keeper/provider/aes_new.rb
|
246
246
|
- lib/crypt_keeper/provider/base.rb
|
247
247
|
- lib/crypt_keeper/provider/mysql_aes_new.rb
|
248
|
+
- lib/crypt_keeper/provider/postgres_base.rb
|
248
249
|
- lib/crypt_keeper/provider/postgres_pgp.rb
|
249
250
|
- lib/crypt_keeper/provider/postgres_pgp_public_key.rb
|
250
251
|
- lib/crypt_keeper/version.rb
|