encrypted_cookie 0.0.1 → 0.0.2
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.
- data.tar.gz.sig +0 -0
- data/Manifest +5 -2
- data/README.markdown +0 -0
- data/Rakefile +1 -1
- data/encrypted_cookie.gemspec +5 -5
- data/lib/encrypted_cookie.rb +66 -15
- data/spec/encrypted_cookie_spec.rb +95 -0
- data/test/demo.rb +15 -0
- metadata +12 -7
- metadata.gz.sig +0 -0
- data/encrypted_cookie_spec.rb +0 -11
data.tar.gz.sig
CHANGED
Binary file
|
data/Manifest
CHANGED
data/README.markdown
ADDED
File without changes
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('encrypted_cookie', '0.0.
|
5
|
+
Echoe.new('encrypted_cookie', '0.0.2') do |p|
|
6
6
|
p.description = "Encrypted session cookies for Rack"
|
7
7
|
p.url = "http://github.com/cvonkleist/encrypted_cookie"
|
8
8
|
p.author = "Christian von Kleist"
|
data/encrypted_cookie.gemspec
CHANGED
@@ -2,18 +2,18 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{encrypted_cookie}
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Christian von Kleist"]
|
9
9
|
s.cert_chain = ["/home/cvk/.gemcert/gem-public_cert.pem"]
|
10
|
-
s.date = %q{2011-03-
|
10
|
+
s.date = %q{2011-03-02}
|
11
11
|
s.description = %q{Encrypted session cookies for Rack}
|
12
12
|
s.email = %q{cvonkleist at-a-place-called gmail.com}
|
13
|
-
s.extra_rdoc_files = ["lib/encrypted_cookie.rb"]
|
14
|
-
s.files = ["Rakefile", "
|
13
|
+
s.extra_rdoc_files = ["README.markdown", "lib/encrypted_cookie.rb"]
|
14
|
+
s.files = ["Manifest", "README.markdown", "Rakefile", "encrypted_cookie.gemspec", "lib/encrypted_cookie.rb", "spec/encrypted_cookie_spec.rb", "test/demo.rb"]
|
15
15
|
s.homepage = %q{http://github.com/cvonkleist/encrypted_cookie}
|
16
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Encrypted_cookie"]
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Encrypted_cookie", "--main", "README.markdown"]
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
s.rubyforge_project = %q{encrypted_cookie}
|
19
19
|
s.rubygems_version = %q{1.3.7}
|
data/lib/encrypted_cookie.rb
CHANGED
@@ -1,27 +1,78 @@
|
|
1
1
|
require 'openssl'
|
2
|
+
require 'rack/session/cookie'
|
2
3
|
|
3
4
|
module Rack
|
4
5
|
module Session
|
5
|
-
class Cookie
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
class EncryptedCookie < Cookie
|
7
|
+
def initialize(app, options={})
|
8
|
+
@app = app
|
9
|
+
@key = options[:key] || "rack.session"
|
10
|
+
@secret = options[:secret]
|
11
|
+
fail "Error! A secret is required to use encrypted cookies. Do something like this:\n\nuse Rack::Session::EncryptedCookie, :secret => YOUR_VERY_LONG_VERY_RANDOM_SECRET_KEY_HERE" unless @secret
|
12
|
+
@default_options = {:domain => nil,
|
13
|
+
:path => "/",
|
14
|
+
:expire_after => nil}.merge(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_session(env)
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
session_data = request.cookies[@key]
|
20
|
+
|
21
|
+
if session_data
|
22
|
+
session_data = decrypt(session_data)
|
23
|
+
session_data, digest = session_data.split("--")
|
24
|
+
session_data = nil unless digest == generate_hmac(session_data)
|
9
25
|
end
|
10
26
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
27
|
+
begin
|
28
|
+
session_data = session_data.unpack("m*").first
|
29
|
+
session_data = Marshal.load(session_data)
|
30
|
+
env["rack.session"] = session_data
|
31
|
+
rescue
|
32
|
+
env["rack.session"] = Hash.new
|
16
33
|
end
|
17
34
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
35
|
+
env["rack.session.options"] = @default_options.dup
|
36
|
+
end
|
37
|
+
|
38
|
+
def commit_session(env, status, headers, body)
|
39
|
+
session_data = Marshal.dump(env["rack.session"])
|
40
|
+
session_data = [session_data].pack("m*")
|
41
|
+
|
42
|
+
session_data = "#{session_data}--#{generate_hmac(session_data)}"
|
43
|
+
|
44
|
+
session_data = encrypt(session_data)
|
45
|
+
|
46
|
+
if session_data.size > (4096 - @key.size)
|
47
|
+
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
|
48
|
+
else
|
49
|
+
options = env["rack.session.options"]
|
50
|
+
cookie = Hash.new
|
51
|
+
cookie[:value] = session_data
|
52
|
+
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
|
53
|
+
Utils.set_cookie_header!(headers, @key, cookie.merge(options))
|
24
54
|
end
|
55
|
+
|
56
|
+
[status, headers, body]
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def encrypt(str)
|
62
|
+
aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').encrypt
|
63
|
+
aes.key = @secret
|
64
|
+
salt = OpenSSL::Random.random_bytes(aes.key_len)
|
65
|
+
iv = OpenSSL::Random.random_bytes(aes.iv_len)
|
66
|
+
[iv + (aes.update(str) << aes.final)].pack('m0')
|
67
|
+
end
|
68
|
+
|
69
|
+
def decrypt(str)
|
70
|
+
str = str.unpack('m0').first
|
71
|
+
aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').decrypt
|
72
|
+
aes.key = @secret
|
73
|
+
iv = str[0, aes.iv_len]
|
74
|
+
crypted_text = str[aes.iv_len..-1]
|
75
|
+
aes.update(crypted_text) << aes.final
|
25
76
|
end
|
26
77
|
end
|
27
78
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
require 'sinatra'
|
3
|
+
require 'rspec'
|
4
|
+
require 'ruby-debug'
|
5
|
+
require 'cgi'
|
6
|
+
require File.dirname(__FILE__) + '/../lib/encrypted_cookie'
|
7
|
+
|
8
|
+
include Rack::Session
|
9
|
+
|
10
|
+
RSpec.configure do |conf|
|
11
|
+
conf.include Rack::Test::Methods
|
12
|
+
end
|
13
|
+
|
14
|
+
class EncryptedApp < Sinatra::Application
|
15
|
+
use Rack::Session::EncryptedCookie, :secret => 'foo' * 10
|
16
|
+
get '/' do
|
17
|
+
"session: " + session.inspect
|
18
|
+
end
|
19
|
+
get '/set/:key/:value' do
|
20
|
+
session[params[:key]] = params[:value]
|
21
|
+
"all set"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# this app has cookie integrity protection, but not encryption
|
26
|
+
class UnencryptedApp < Sinatra::Application
|
27
|
+
use Rack::Session::Cookie, :secret => 'foo' * 10
|
28
|
+
get '/' do
|
29
|
+
"session: " + session.inspect
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe EncryptedCookie do
|
34
|
+
it 'should fail if no secret is specified' do
|
35
|
+
lambda { EncryptedCookie.new(nil) }.should raise_error(/A secret is required/)
|
36
|
+
lambda { EncryptedCookie.new(nil, :secret => 'foo') }.should_not raise_error(/A secret is required/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe EncryptedApp do
|
41
|
+
def app
|
42
|
+
EncryptedApp
|
43
|
+
end
|
44
|
+
it "should not include unencrypted marshal'd data" do
|
45
|
+
get '/'
|
46
|
+
last_response.body.should == 'session: {}'
|
47
|
+
last_response.headers['Set-Cookie'].should_not include("BAh7AA")
|
48
|
+
end
|
49
|
+
it "should make decryptable session data" do
|
50
|
+
get '/set/foo/bar'
|
51
|
+
last_response.body.should == 'all set'
|
52
|
+
get '/'
|
53
|
+
last_response.body.should == 'session: {"foo"=>"bar"}'
|
54
|
+
last_response.headers['Set-Cookie'].should_not include("BAh7AA")
|
55
|
+
|
56
|
+
data = last_response.headers['Set-Cookie'][/rack.session=(.*?);/, 1]
|
57
|
+
str = CGI.unescape(data).unpack('m0').first
|
58
|
+
aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').decrypt
|
59
|
+
aes.key = 'foo' * 10
|
60
|
+
iv = str[0, aes.iv_len]
|
61
|
+
crypted_text = str[aes.iv_len..-1]
|
62
|
+
|
63
|
+
plaintext = (aes.update(crypted_text) << aes.final)
|
64
|
+
base64_marshal_data, hmac = plaintext.split('--')
|
65
|
+
session_hash = Marshal.load(base64_marshal_data.unpack('m0').first)
|
66
|
+
session_hash.should == {"foo" => "bar"}
|
67
|
+
end
|
68
|
+
it "should make encrypted session data that can't be decrypted with the wrong key" do
|
69
|
+
get '/set/foo/bar'
|
70
|
+
last_response.body.should == 'all set'
|
71
|
+
get '/'
|
72
|
+
last_response.body.should == 'session: {"foo"=>"bar"}'
|
73
|
+
last_response.headers['Set-Cookie'].should_not include("BAh7AA")
|
74
|
+
|
75
|
+
data = last_response.headers['Set-Cookie'][/rack.session=(.*?);/, 1]
|
76
|
+
str = CGI.unescape(data).unpack('m0').first
|
77
|
+
aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').decrypt
|
78
|
+
aes.key = 'bar' * 10
|
79
|
+
iv = str[0, aes.iv_len]
|
80
|
+
crypted_text = str[aes.iv_len..-1]
|
81
|
+
|
82
|
+
lambda { plaintext = (aes.update(crypted_text) << aes.final) }.should raise_error("bad decrypt")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe UnencryptedApp do
|
87
|
+
def app
|
88
|
+
UnencryptedApp
|
89
|
+
end
|
90
|
+
it "should include unencrypted marshal'd data" do
|
91
|
+
get '/'
|
92
|
+
last_response.body.should == 'session: {}'
|
93
|
+
last_response.headers['Set-Cookie'].should include("BAh7AA")
|
94
|
+
end
|
95
|
+
end
|
data/test/demo.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require '../lib/encrypted_cookie'
|
3
|
+
|
4
|
+
use Rack::Session::EncryptedCookie,
|
5
|
+
:key => 'rack.session',
|
6
|
+
:secret => 'ASDFASDFASDJFsakdfji2j3oij2o3ij4l12kj3'
|
7
|
+
|
8
|
+
get '/' do
|
9
|
+
"session = " + session.inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
get '/set/:data' do
|
13
|
+
session[:data] = params[:data]
|
14
|
+
redirect '/'
|
15
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: encrypted_cookie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Christian von Kleist
|
@@ -36,7 +36,7 @@ cert_chain:
|
|
36
36
|
DnOM3kD7rptG2g==
|
37
37
|
-----END CERTIFICATE-----
|
38
38
|
|
39
|
-
date: 2011-03-
|
39
|
+
date: 2011-03-02 00:00:00 -05:00
|
40
40
|
default_executable:
|
41
41
|
dependencies: []
|
42
42
|
|
@@ -47,13 +47,16 @@ executables: []
|
|
47
47
|
extensions: []
|
48
48
|
|
49
49
|
extra_rdoc_files:
|
50
|
+
- README.markdown
|
50
51
|
- lib/encrypted_cookie.rb
|
51
52
|
files:
|
52
|
-
- Rakefile
|
53
|
-
- encrypted_cookie_spec.rb
|
54
|
-
- lib/encrypted_cookie.rb
|
55
53
|
- Manifest
|
54
|
+
- README.markdown
|
55
|
+
- Rakefile
|
56
56
|
- encrypted_cookie.gemspec
|
57
|
+
- lib/encrypted_cookie.rb
|
58
|
+
- spec/encrypted_cookie_spec.rb
|
59
|
+
- test/demo.rb
|
57
60
|
has_rdoc: true
|
58
61
|
homepage: http://github.com/cvonkleist/encrypted_cookie
|
59
62
|
licenses: []
|
@@ -64,6 +67,8 @@ rdoc_options:
|
|
64
67
|
- --inline-source
|
65
68
|
- --title
|
66
69
|
- Encrypted_cookie
|
70
|
+
- --main
|
71
|
+
- README.markdown
|
67
72
|
require_paths:
|
68
73
|
- lib
|
69
74
|
required_ruby_version: !ruby/object:Gem::Requirement
|
metadata.gz.sig
CHANGED
Binary file
|
data/encrypted_cookie_spec.rb
DELETED