encookie 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +28 -0
- data/Rakefile +1 -0
- data/encookie.gemspec +22 -0
- data/lib/encookie.rb +7 -0
- data/lib/encookie/cookie.rb +66 -0
- data/lib/encookie/cryptor.rb +26 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 84167f668043958eead4609ed8af9aac231f59bb
|
4
|
+
data.tar.gz: d93981c2c83e486aff8f7b8b0f291a9e533e1348
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1487a5f3d2dd9a31201ef7c6a6ec31e5dd4f0e28b34f0233d7917e11239f90fd3ab91432ad80d4160b6eb86a29a1b1f41e0feecf91f74d9dbc23a9a3dc345715
|
7
|
+
data.tar.gz: d90f0bc9724a1ad6fa90763654225b063c26ed10a741838085689a1f83f7f49d4cfebacc7d7a5e64e68b0b268ce781f17f1473b60dee11b9b58a37cfc91d0bda
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Encookie
|
2
|
+
|
3
|
+
`encookie` is a Ruby gem that provides a Rack session middleware implementation.
|
4
|
+
This implementation stores all data in the HTTP cookies - like `Rack::Session::Cookie`,
|
5
|
+
but also encrypts the contents of the cookies so it cannot be read by the user.
|
6
|
+
|
7
|
+
Encryption **and** authentication is provided by OpenSSL's `aes-128-gcm` cipher.
|
8
|
+
This means that there is no need for a separate MAC step or a second key.
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
# Gemfile
|
14
|
+
gem 'encookie'
|
15
|
+
|
16
|
+
# config.ru
|
17
|
+
require 'encookie'
|
18
|
+
key = ['7881e46e1e40484756485019038703c2'].pack 'H*'
|
19
|
+
use Rack::Session::Encookie, key: key
|
20
|
+
|
21
|
+
# app.rb
|
22
|
+
get '/' do
|
23
|
+
session[:yourdata] = 'super secret persistent data'
|
24
|
+
end
|
25
|
+
````
|
26
|
+
|
27
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/glassechidna/encookie.
|
28
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/encookie.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'encookie'
|
5
|
+
spec.version = '0.0.1'
|
6
|
+
spec.authors = ['Aidan Steele']
|
7
|
+
spec.email = ['aidan.steele@glassechidna.com.au']
|
8
|
+
|
9
|
+
spec.summary = %q{Write a short summary, because Rubygems requires one.}
|
10
|
+
# spec.homepage = 'TODO: Put your gem's website or public repo URL here.'
|
11
|
+
|
12
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
13
|
+
spec.bindir = 'exe'
|
14
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
15
|
+
spec.require_paths = ['lib']
|
16
|
+
|
17
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
18
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
19
|
+
|
20
|
+
spec.add_dependency 'rack'
|
21
|
+
spec.add_dependency 'multi_json'
|
22
|
+
end
|
data/lib/encookie.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Rack
|
2
|
+
module Session
|
3
|
+
class Encookie
|
4
|
+
def initialize(app, options={})
|
5
|
+
@app = app
|
6
|
+
@name = options[:name] || 'rack.session'
|
7
|
+
@key = options[:key]
|
8
|
+
@default_options = { domain: nil, path: '/'}.merge(options)
|
9
|
+
fail 'A secret is required' unless @key
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
load_session env
|
14
|
+
status, headers, body = @app.call env
|
15
|
+
commit_session env, status, headers, body
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_session(env)
|
19
|
+
request = Rack::Request.new env
|
20
|
+
env['rack.session.options'] = @default_options.dup
|
21
|
+
|
22
|
+
cookieval = request.cookies[@name]
|
23
|
+
enc = cookieval_to_encrypted cookieval
|
24
|
+
json = ::Encookie::Cryptor.decrypt ciphertext: enc.ciphertext, key: @key, iv: enc.iv, auth_tag: enc.auth_tag
|
25
|
+
session_data = MultiJson.load json, symbolize_keys: true
|
26
|
+
|
27
|
+
env['rack.session'] = session_data
|
28
|
+
rescue
|
29
|
+
env['rack.session'] = Hash.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def commit_session(env, status, headers, body)
|
33
|
+
options = env['rack.session.options']
|
34
|
+
|
35
|
+
session_data = env['rack.session']
|
36
|
+
json = MultiJson.dump session_data
|
37
|
+
enc = ::Encookie::Cryptor.encrypt plaintext: json, key: @key
|
38
|
+
cookieval = encrypted_to_cookieval enc
|
39
|
+
|
40
|
+
if session_data.size > (4096 - @name.size)
|
41
|
+
env['rack.errors'].puts 'Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.'
|
42
|
+
else
|
43
|
+
cookie = Hash.new
|
44
|
+
cookie[:value] = cookieval
|
45
|
+
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
|
46
|
+
Utils.set_cookie_header! headers, @name, cookie.merge(options)
|
47
|
+
end
|
48
|
+
|
49
|
+
[status, headers, body]
|
50
|
+
end
|
51
|
+
|
52
|
+
def encrypted_to_cookieval(encrypted)
|
53
|
+
combined = encrypted.iv + encrypted.auth_tag + encrypted.ciphertext
|
54
|
+
Base64.urlsafe_encode64 combined
|
55
|
+
end
|
56
|
+
|
57
|
+
def cookieval_to_encrypted(cookieval)
|
58
|
+
combined = Base64.urlsafe_decode64 cookieval
|
59
|
+
iv = combined[0..11]
|
60
|
+
auth_tag = combined[12..27]
|
61
|
+
ciphertext = combined[28..-1]
|
62
|
+
::Encookie::Cryptor::Encrypted.new ciphertext, iv, auth_tag
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Encookie
|
2
|
+
class Cryptor
|
3
|
+
Encrypted = Struct.new(:ciphertext, :iv, :auth_tag)
|
4
|
+
|
5
|
+
def self.encrypt(plaintext:, key:)
|
6
|
+
cipher = OpenSSL::Cipher.new 'aes-128-gcm'
|
7
|
+
cipher.encrypt
|
8
|
+
cipher.key = key
|
9
|
+
iv = cipher.random_iv
|
10
|
+
cipher.auth_data = ''
|
11
|
+
ciphertext = cipher.update(plaintext) + cipher.final
|
12
|
+
auth_tag = cipher.auth_tag
|
13
|
+
Encrypted.new(ciphertext, iv, auth_tag)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.decrypt(ciphertext:, key:, iv:, auth_tag:)
|
17
|
+
cipher = OpenSSL::Cipher.new 'aes-128-gcm'
|
18
|
+
cipher.decrypt
|
19
|
+
cipher.key = key
|
20
|
+
cipher.iv = iv
|
21
|
+
cipher.auth_tag = auth_tag
|
22
|
+
cipher.auth_data = ''
|
23
|
+
cipher.update(ciphertext) + cipher.final
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: encookie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aidan Steele
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: multi_json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- aidan.steele@glassechidna.com.au
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".travis.yml"
|
78
|
+
- Gemfile
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- encookie.gemspec
|
82
|
+
- lib/encookie.rb
|
83
|
+
- lib/encookie/cookie.rb
|
84
|
+
- lib/encookie/cryptor.rb
|
85
|
+
homepage:
|
86
|
+
licenses: []
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.4.5.1
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Write a short summary, because Rubygems requires one.
|
108
|
+
test_files: []
|