validas-encrypted_cookie_store 0.4.0

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/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rails', '~>3.2.0'
4
+
5
+ group :development do
6
+ gem 'rake'
7
+ gem 'bundler'
8
+ gem 'rspec', "~> 2.6.0"
9
+ gem 'jeweler'
10
+ end
11
+
data/Gemfile.lock ADDED
@@ -0,0 +1,104 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionmailer (3.2.8)
5
+ actionpack (= 3.2.8)
6
+ mail (~> 2.4.4)
7
+ actionpack (3.2.8)
8
+ activemodel (= 3.2.8)
9
+ activesupport (= 3.2.8)
10
+ builder (~> 3.0.0)
11
+ erubis (~> 2.7.0)
12
+ journey (~> 1.0.4)
13
+ rack (~> 1.4.0)
14
+ rack-cache (~> 1.2)
15
+ rack-test (~> 0.6.1)
16
+ sprockets (~> 2.1.3)
17
+ activemodel (3.2.8)
18
+ activesupport (= 3.2.8)
19
+ builder (~> 3.0.0)
20
+ activerecord (3.2.8)
21
+ activemodel (= 3.2.8)
22
+ activesupport (= 3.2.8)
23
+ arel (~> 3.0.2)
24
+ tzinfo (~> 0.3.29)
25
+ activeresource (3.2.8)
26
+ activemodel (= 3.2.8)
27
+ activesupport (= 3.2.8)
28
+ activesupport (3.2.8)
29
+ i18n (~> 0.6)
30
+ multi_json (~> 1.0)
31
+ arel (3.0.2)
32
+ builder (3.0.4)
33
+ diff-lcs (1.1.3)
34
+ erubis (2.7.0)
35
+ git (1.2.5)
36
+ hike (1.2.1)
37
+ i18n (0.6.1)
38
+ jeweler (1.8.4)
39
+ bundler (~> 1.0)
40
+ git (>= 1.2.5)
41
+ rake
42
+ rdoc
43
+ journey (1.0.4)
44
+ json (1.7.5)
45
+ mail (2.4.4)
46
+ i18n (>= 0.4.0)
47
+ mime-types (~> 1.16)
48
+ treetop (~> 1.4.8)
49
+ mime-types (1.19)
50
+ multi_json (1.3.6)
51
+ polyglot (0.3.3)
52
+ rack (1.4.1)
53
+ rack-cache (1.2)
54
+ rack (>= 0.4)
55
+ rack-ssl (1.3.2)
56
+ rack
57
+ rack-test (0.6.2)
58
+ rack (>= 1.0)
59
+ rails (3.2.8)
60
+ actionmailer (= 3.2.8)
61
+ actionpack (= 3.2.8)
62
+ activerecord (= 3.2.8)
63
+ activeresource (= 3.2.8)
64
+ activesupport (= 3.2.8)
65
+ bundler (~> 1.0)
66
+ railties (= 3.2.8)
67
+ railties (3.2.8)
68
+ actionpack (= 3.2.8)
69
+ activesupport (= 3.2.8)
70
+ rack-ssl (~> 1.3.2)
71
+ rake (>= 0.8.7)
72
+ rdoc (~> 3.4)
73
+ thor (>= 0.14.6, < 2.0)
74
+ rake (0.9.2.2)
75
+ rdoc (3.12)
76
+ json (~> 1.4)
77
+ rspec (2.6.0)
78
+ rspec-core (~> 2.6.0)
79
+ rspec-expectations (~> 2.6.0)
80
+ rspec-mocks (~> 2.6.0)
81
+ rspec-core (2.6.4)
82
+ rspec-expectations (2.6.0)
83
+ diff-lcs (~> 1.1.2)
84
+ rspec-mocks (2.6.0)
85
+ sprockets (2.1.3)
86
+ hike (~> 1.2)
87
+ rack (~> 1.0)
88
+ tilt (~> 1.1, != 1.3.0)
89
+ thor (0.16.0)
90
+ tilt (1.3.3)
91
+ treetop (1.4.12)
92
+ polyglot
93
+ polyglot (>= 0.3.1)
94
+ tzinfo (0.3.34)
95
+
96
+ PLATFORMS
97
+ ruby
98
+
99
+ DEPENDENCIES
100
+ bundler
101
+ jeweler
102
+ rails (~> 3.2.0)
103
+ rake
104
+ rspec (~> 2.6.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2009 - 2010 Phusion
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name of the Phusion nor the names of its contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,114 @@
1
+ [![Build Status](https://secure.travis-ci.org/validas/encrypted_cookie_store.png)](http://travis-ci.org/validas/encrypted_cookie_store)
2
+ EncryptedCookieStore
3
+ ====================
4
+ EncryptedCookieStore is similar to Ruby on Rails's CookieStore (it saves
5
+ session data in a cookie), but it uses encryption so that people can't read
6
+ what's in the session data. This makes it possible to store sensitive data
7
+ in the session.
8
+
9
+ This version of EncryptedCookieStore is written for Rails 3.0.0+. It will not work with Rails 3.0.0.beta3 or earlier. It does not yet work with Rails 3.1. It has been tested with:
10
+
11
+ * 3.0.0
12
+ * 3.0.7
13
+ * 3.0.8.rc4
14
+
15
+ The original version for Rails 2.3 can be found here: https://github.com/FooBarWidget/encrypted_cookie_store
16
+
17
+ For a version that probably works with Rails 3.0.0.beta - 3.0.0.beta3, check here: https://github.com/twoism-dev/encrypted_cookie_store
18
+
19
+ Installation and usage
20
+ ----------------------
21
+
22
+ First, install it:
23
+
24
+ gem install scottwb-encrypted_cookie_store
25
+
26
+ Then, add it to you bundler Gemfile:
27
+
28
+ gem 'scottwb-encrypted_cookie_store', :require => 'encrypted_cookie_store'
29
+
30
+ Then edit `config/initializers/session_store.rb` and set your session store to
31
+ EncryptedCookieStore:
32
+
33
+ MyApp::Application.config.session_store(
34
+ EncryptedCookieStore::EncryptedCookieStore,
35
+ :key => '_myapp_session',
36
+ :encryption_key => '966a4....'
37
+ )
38
+
39
+ The encryption key *must* be a hexadecimal string of exactly 32 bytes. It
40
+ should be entirely random, because otherwise it can make the encryption weak.
41
+
42
+ You can generate a new encryption key by running `rake secret:encryption_key`.
43
+ This command will output a random encryption key that you can then copy and
44
+ paste into your environment.rb.
45
+
46
+ You also need to make sure you have a secret token defined in `config/initializers/secret_token.rb`, just as you work for the standard CookieStore, e.g.:
47
+
48
+ MyApp::Application.config.secret_token = 'f75bb....'
49
+
50
+ Operational details
51
+ -------------------
52
+ Upon generating cookie data, EncryptedCookieStore generates a new, random
53
+ initialization vector for encrypting the session data. This initialization
54
+ vector is then encrypted with 128-bit AES in ECB mode. The session data is
55
+ first protected with an HMAC to prevent tampering. The session data, along
56
+ with the HMAC, are then encrypted using 256-bit AES in CFB mode with the
57
+ generated initialization vector. This encrypted session data + HMAC are
58
+ then stored, along with the encrypted initialization vector, into the cookie.
59
+
60
+ Upon unmarshalling the cookie data, EncryptedCookieStore decrypts the
61
+ encrypted initialization vector and use that to decrypt the encrypted
62
+ session data + HMAC. The decrypted session data is then verified against
63
+ the HMAC.
64
+
65
+ The reason why HMAC verification occurs after decryption instead of before
66
+ decryption is because we want to be able to detect changes to the encryption
67
+ key and changes to the HMAC secret key, as well as migrations from CookieStore.
68
+ Verifying after decryption allows us to automatically invalidate such old
69
+ session cookies.
70
+
71
+ EncryptedCookieStore is quite fast: it is able to marshal and unmarshal a
72
+ simple session object 5000 times in 8.7 seconds on a MacBook Pro with a 2.4
73
+ Ghz Intel Core 2 Duo (in battery mode). This is about 0.174 ms per
74
+ marshal+unmarshal action. See `rake benchmark` in the EncryptedCookieStore
75
+ sources for details.
76
+
77
+ EncryptedCookieStore vs other session stores
78
+ --------------------------------------------
79
+ EncryptedCookieStore inherits all the benefits of CookieStore:
80
+
81
+ * It works out of the box without the need to setup a seperate data store (e.g. database table, daemon, etc).
82
+ * It does not require any maintenance. Old, stale sessions do not need to be manually cleaned up, as is the case with PStore and ActiveRecordStore.
83
+ * Compared to MemCacheStore, EncryptedCookieStore can "hold" an infinite number of sessions at any time.
84
+ * It can be scaled across multiple servers without any additional setup.
85
+ * It is fast.
86
+ * It is more secure than CookieStore because it allows you to store sensitive data in the session.
87
+
88
+ There are of course drawbacks as well:
89
+
90
+ * It is prone to session replay attacks. These kind of attacks are explained in the [Ruby on Rails Security Guide](http://guides.rubyonrails.org/security.html#session-storage). Therefore you should never store anything along the lines of `is_admin` in the session.
91
+ * You can store at most a little less than 4 KB of data in the session because that's the size limit of a cookie. "A little less" because EncryptedCookieStore also stores a small amount of bookkeeping data in the cookie.
92
+ * Although encryption makes it more secure than CookieStore, there's still a chance that a bug in EncryptedCookieStore renders it insecure. We welcome everyone to audit this code. There's also a chance that weaknesses in AES are found in the near future which render it insecure. If you are storing *really* sensitive information in the session, e.g. social security numbers, or plans for world domination, then you should consider using ActiveRecordStore or some other server-side store.
93
+
94
+ JRuby: Illegal Key Size error
95
+ -----------------------------
96
+ If you get this error (and your code works with MRI)...
97
+
98
+ Illegal key size
99
+
100
+ [...]/vendor/plugins/encrypted_cookie_store/lib/encrypted_cookie_store.rb:62:in `marshal'
101
+
102
+ ...then it probably means you don't have the "unlimited strength" policy files
103
+ installed for your JVM.
104
+ [Download and install them.](http://www.ngs.ac.uk/tools/jcepolicyfiles)
105
+ You probably have the "strong" version if they are already there.
106
+
107
+ As a workaround, you can change the cipher type from 256-bit AES to 128-bit by
108
+ inserting the following in `config/initializer/session_store.rb`:
109
+
110
+ EncryptedCookieStore.data_cipher_type = 'aes-128-cfb'.freeze # was 256
111
+
112
+ Please note that after changing to 128-bit AES, EncryptedCookieStore still
113
+ requires a 32 bytes hexadecimal encryption key, although only half of the key
114
+ is actually used.
data/Rakefile ADDED
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ gem.name = "validas-encrypted_cookie_store"
16
+ gem.homepage = "https://github.com/validas/encrypted_cookie_store"
17
+ gem.summary = "A Rails 3.1 & 3.2 version of Encrypted Cookie Store by FooBarWidget"
18
+ gem.description = "A Rails 3.1 & 3.2 version of Encrypted Cookie Store by FooBarWidget"
19
+ gem.email = "scottwb@gmail.com"
20
+ gem.authors = ["FooBarWidget", "Scott W. Bradley", "Validas Engineering Team"]
21
+ end
22
+ Jeweler::RubygemsDotOrgTasks.new
23
+
24
+
25
+ require 'rspec/core'
26
+ require 'rspec/core/rake_task'
27
+ RSpec::Core::RakeTask.new(:spec) do |spec|
28
+ spec.pattern = FileList['spec/**/*_spec.rb']
29
+ end
30
+ task :default => :spec
31
+
32
+
33
+ desc "Run benchmark"
34
+ task :benchmark do
35
+ $LOAD_PATH.unshift(File.expand_path("lib"))
36
+ require 'rubygems'
37
+ require 'benchmark'
38
+ require "rails"
39
+ require 'action_controller'
40
+ require 'encrypted_cookie_store'
41
+
42
+ secret = "b6a30e998806a238c4bad45cc720ed55e56e50d9f00fff58552e78a20fe8262df61" <<
43
+ "42fcfdb0676018bb9767ed560d4a624fb7f3603b4e53c77ec189ae3853bd1"
44
+ encryption_key = "dd458e790c3b995e3606384c58efc53da431db892f585aa3ca2a17eabe6df75b"
45
+ store = EncryptedCookieStore::EncryptedCookieStore.new(
46
+ nil,
47
+ :secret => secret,
48
+ :key => 'my_app',
49
+ :encryption_key => encryption_key
50
+ )
51
+ object = {
52
+ :hello => "world",
53
+ :user_id => 1234,
54
+ :is_admin => true,
55
+ :shopping_cart => ["Tea x 1", "Carrots x 13", "Pocky x 20", "Pen x 4"],
56
+ :session_id => "b6a30e998806a238c4bad45cc720ed55e56e50d9f00ff"
57
+ }
58
+ count = 50_000
59
+
60
+ puts "Marshalling and unmarshalling #{count} times..."
61
+ result = Benchmark.measure do
62
+ count.times do
63
+ data = store.send(:set_session, nil, nil, object)
64
+ store.send(:unmarshal, data)
65
+ end
66
+ end
67
+ puts result
68
+ printf "%.3f ms per marshal+unmarshal action\n", result.real * 1000 / count
69
+ end
70
+
71
+
72
+ require "rdoc/task"
73
+ Rake::RDocTask.new do |rdoc|
74
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
75
+
76
+ rdoc.rdoc_dir = "rdoc"
77
+ rdoc.title = "validas-encrypted_cookie_store #{version}"
78
+ rdoc.rdoc_files.include("README*")
79
+ rdoc.rdoc_files.include("lib/**/*.rb")
80
+ #rdoc.main = "README.md"
81
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -0,0 +1,2 @@
1
+ require 'encrypted_cookie_store/railtie'
2
+ require 'encrypted_cookie_store/encrypted_cookie_store'
@@ -0,0 +1,3 @@
1
+ module EncryptedCookieStoreConstants
2
+ ENCRYPTION_KEY_SIZE = 32
3
+ end
@@ -0,0 +1,145 @@
1
+ require 'openssl'
2
+ require 'encrypted_cookie_store/constants'
3
+
4
+ module EncryptedCookieStore
5
+ class EncryptedCookieStore < ActionDispatch::Session::CookieStore
6
+ OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
7
+ include EncryptedCookieStoreConstants
8
+
9
+ class << self
10
+ attr_accessor :iv_cipher_type
11
+ attr_accessor :data_cipher_type
12
+ end
13
+
14
+ self.iv_cipher_type = "aes-128-ecb".freeze
15
+ self.data_cipher_type = "aes-256-cfb".freeze
16
+
17
+ def initialize(app, options = {})
18
+ ensure_encryption_key_secure(options[:encryption_key])
19
+ @encryption_key = unhex(options[:encryption_key]).freeze
20
+ @iv_cipher = OpenSSL::Cipher::Cipher.new(EncryptedCookieStore.iv_cipher_type)
21
+ @data_cipher = OpenSSL::Cipher::Cipher.new(EncryptedCookieStore.data_cipher_type)
22
+ super(app, options)
23
+ end
24
+
25
+ private
26
+ # Like ActiveSupport::MessageVerifier, but does not base64-encode data.
27
+ class MessageVerifier
28
+ def initialize(secret, digest = 'SHA1')
29
+ @secret = secret
30
+ @digest = digest
31
+ end
32
+
33
+ def verify(signed_message)
34
+ digest, data = signed_message.split("--", 2)
35
+ if digest != generate_digest(data)
36
+ raise ActiveSupport::MessageVerifier::InvalidSignature
37
+ else
38
+ Marshal.load(data)
39
+ end
40
+ end
41
+
42
+ def generate(value)
43
+ data = Marshal.dump(value)
44
+ digest = generate_digest(data)
45
+ "#{digest}--#{data}"
46
+ end
47
+
48
+ private
49
+ def generate_digest(data)
50
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data)
51
+ end
52
+ end
53
+
54
+ def set_session(env, sid, session_data, options={})
55
+ # We hmac-then-encrypt instead of encrypt-then-hmac so that we
56
+ # can properly detect:
57
+ # - changes to the encryption key or initialization vector
58
+ # - a migration from the unencrypted CookieStore.
59
+ #
60
+ # Being able to detect these allows us to invalidate the old session data.
61
+
62
+ @iv_cipher.encrypt
63
+ @data_cipher.encrypt
64
+ @iv_cipher.key = @encryption_key
65
+ @data_cipher.key = @encryption_key
66
+
67
+ clear_session_data = super(env, sid, session_data, {})
68
+ iv = @data_cipher.random_iv
69
+ @data_cipher.iv = iv
70
+ encrypted_iv = @iv_cipher.update(iv) << @iv_cipher.final
71
+ encrypted_session_data = @data_cipher.update(Marshal.dump(clear_session_data)) << @data_cipher.final
72
+
73
+ "#{base64(encrypted_iv)}--#{base64(encrypted_session_data)}"
74
+ end
75
+
76
+ def unpacked_cookie_data(env)
77
+ env["action_dispatch.request.unsigned_session_cookie"] ||= begin
78
+ stale_session_check! do
79
+ request = ActionDispatch::Request.new(env)
80
+ if (data = request.cookie_jar.signed[@key]) && data.is_a?(String)
81
+ unmarshal(data)
82
+ else
83
+ {}
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def unmarshal(cookie)
90
+ if cookie
91
+ b64_encrypted_iv, b64_encrypted_session_data = cookie.split("--", 2)
92
+ if b64_encrypted_iv && b64_encrypted_session_data
93
+ encrypted_iv = ::Base64.strict_decode64(b64_encrypted_iv)
94
+ encrypted_session_data = ::Base64.strict_decode64(b64_encrypted_session_data)
95
+
96
+ @iv_cipher.decrypt
97
+ @iv_cipher.key = @encryption_key
98
+ iv = @iv_cipher.update(encrypted_iv) << @iv_cipher.final
99
+
100
+ @data_cipher.decrypt
101
+ @data_cipher.key = @encryption_key
102
+ @data_cipher.iv = iv
103
+ session_data = Marshal.load(@data_cipher.update(encrypted_session_data) << @data_cipher.final) rescue nil
104
+ end
105
+ else
106
+ nil
107
+ end
108
+ rescue OpenSSLCipherError
109
+ nil
110
+ end
111
+
112
+ # To prevent users from using an insecure encryption key like "Password" we make sure that the
113
+ # encryption key they've provided is at least 30 characters in length.
114
+ def ensure_encryption_key_secure(encryption_key)
115
+ if encryption_key.blank?
116
+ raise ArgumentError, "An encryption key is required for encrypting the " +
117
+ "cookie session data. Please set config.action_controller.session = { " +
118
+ "..., :encryption_key => \"some random string of exactly " +
119
+ "#{ENCRYPTION_KEY_SIZE * 2} bytes\", ... } in config/environment.rb"
120
+ end
121
+
122
+ if encryption_key.size != ENCRYPTION_KEY_SIZE * 2
123
+ raise ArgumentError, "The EncryptedCookieStore encryption key must be a " +
124
+ "hexadecimal string of exactly #{ENCRYPTION_KEY_SIZE * 2} bytes. " +
125
+ "The value that you've provided, \"#{encryption_key}\", is " +
126
+ "#{encryption_key.size} bytes. You could use the following (randomly " +
127
+ "generated) string as encryption key: " +
128
+ SecureRandom.hex(ENCRYPTION_KEY_SIZE)
129
+ end
130
+ end
131
+
132
+ def verifier_for(secret, digest)
133
+ key = secret.respond_to?(:call) ? secret.call : secret
134
+ MessageVerifier.new(key, digest)
135
+ end
136
+
137
+ def base64(data)
138
+ ::Base64.strict_encode64(data)
139
+ end
140
+
141
+ def unhex(hex_data)
142
+ [hex_data].pack("H*")
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,10 @@
1
+ module EncryptedCookieStore
2
+ class Railtie < Rails::Railtie
3
+ initializer "encrypted_cookie_store_railtie.boot" do |app|
4
+ end
5
+
6
+ rake_tasks do
7
+ load 'tasks/encrypted_cookie_store.rake'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ namespace :secret do
2
+ desc "Generate an encryption key for EncryptedCookieStore that's cryptographically secure."
3
+ task :encryption_key do
4
+ require 'encrypted_cookie_store/constants'
5
+ puts ActiveSupport::SecureRandom.hex(EncryptedCookieStoreConstants::ENCRYPTION_KEY_SIZE)
6
+ end
7
+ end
@@ -0,0 +1,88 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
2
+ require 'rubygems'
3
+ gem 'rails', '>= 3.0.0'
4
+ require 'rails'
5
+ require 'action_controller'
6
+ require 'encrypted_cookie_store'
7
+
8
+ describe EncryptedCookieStore::EncryptedCookieStore do
9
+ SECRET = "b6a30e998806a238c4bad45cc720ed55e56e50d9f00fff58552e78a20fe8262df61" <<
10
+ "42fcfdb0676018bb9767ed560d4a624fb7f3603b4e53c77ec189ae3853bd1"
11
+ GOOD_ENCRYPTION_KEY = "dd458e790c3b995e3606384c58efc53da431db892f585aa3ca2a17eabe6df75b"
12
+ ANOTHER_GOOD_ENCRYPTION_KEY = "ce6a45c34607d2048d735b0a31a769de4e1512eb83c7012059a66937158a8975"
13
+ OBJECT = { :user_id => 123, :admin => true, :message => "hello world!" }
14
+
15
+ def create(options = {})
16
+ EncryptedCookieStore::EncryptedCookieStore.new(nil, options.reverse_merge(
17
+ :key => 'key',
18
+ :secret => SECRET
19
+ ))
20
+ end
21
+
22
+ it "checks whether an encryption key is given" do
23
+ lambda { create }.should raise_error(ArgumentError, /encryption key is required/)
24
+ end
25
+
26
+ it "checks whether the encryption key has the correct size" do
27
+ encryption_key = "too small"
28
+ block = lambda { create(:encryption_key => encryption_key) }
29
+ block.should raise_error(ArgumentError, /must be a hexadecimal string of exactly \d+ bytes/)
30
+ end
31
+
32
+ specify "marshalling and unmarshalling data works" do
33
+ data = create(:encryption_key => GOOD_ENCRYPTION_KEY).send(:set_session, nil, nil, OBJECT)
34
+ object = create(:encryption_key => GOOD_ENCRYPTION_KEY).send(:unmarshal, data)
35
+ object[:user_id].should == 123
36
+ object[:admin].should be_true
37
+ object[:message].should == "hello world!"
38
+ end
39
+
40
+ it "uses a different initialization vector every time data is marshalled" do
41
+ store = create(:encryption_key => GOOD_ENCRYPTION_KEY)
42
+ data1 = store.send(:set_session, nil, nil, OBJECT)
43
+ data2 = store.send(:set_session, nil, nil, OBJECT)
44
+ data3 = store.send(:set_session, nil, nil, OBJECT)
45
+ data4 = store.send(:set_session, nil, nil, OBJECT)
46
+ data1.should_not == data2
47
+ data1.should_not == data3
48
+ data1.should_not == data4
49
+ end
50
+
51
+ it "invalidates the data if the encryption key is changed" do
52
+ data = create(:encryption_key => GOOD_ENCRYPTION_KEY).send(:set_session, nil, nil, OBJECT)
53
+ object = create(:encryption_key => ANOTHER_GOOD_ENCRYPTION_KEY).send(:unmarshal, data)
54
+ object.should be_nil
55
+ end
56
+
57
+ it "invalidates the data if the IV cannot be decrypted" do
58
+ store = create(:encryption_key => GOOD_ENCRYPTION_KEY)
59
+ data = store.send(:set_session, nil, nil, OBJECT)
60
+ iv_cipher = store.instance_variable_get(:@iv_cipher)
61
+ iv_cipher.should_receive(:update).and_raise(EncryptedCookieStore::EncryptedCookieStore::OpenSSLCipherError)
62
+ store.send(:unmarshal, data).should be_nil
63
+ end
64
+
65
+ # FIXME: This test case is broken. The super classes' structure have changed since Rails 3.0.0.beta3
66
+ # and we're no longer getting a string to unmarshal, since this have been pushed up to rack.
67
+ #it "invalidates the data if we just migrated from CookieStore" do
68
+ # old_store = ActionDispatch::Session::CookieStore.new(nil, :key => 'key', :secret => SECRET)
69
+ # legacy_data = old_store.send(:set_session, nil, nil, OBJECT)
70
+ # store = create(:encryption_key => GOOD_ENCRYPTION_KEY)
71
+ # store.send(:unmarshal, legacy_data).should be_nil
72
+ #end
73
+
74
+ it "invalidates the data if it was tampered with" do
75
+ store = create(:encryption_key => GOOD_ENCRYPTION_KEY)
76
+ data = store.send(:set_session, nil, nil, OBJECT)
77
+ b64_encrypted_iv, b64_encrypted_session_data = data.split("--", 2)
78
+ b64_encrypted_session_data[0..1] = "AA"
79
+ data = "#{b64_encrypted_iv}--#{b64_encrypted_session_data}"
80
+ store.send(:unmarshal, data).should be_nil
81
+ end
82
+
83
+ it "invalidates the data if it looks like garbage" do
84
+ store = create(:encryption_key => GOOD_ENCRYPTION_KEY)
85
+ garbage = "\202d\3477 jTf\274\360\200z\355\334N3\001\0036\321qLu\027\320\325*%:%\270D"
86
+ store.send(:unmarshal, garbage).should be_nil
87
+ end
88
+ end
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "validas-encrypted_cookie_store"
8
+ s.version = "0.4.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["FooBarWidget", "Scott W. Bradley", "Validas Engineering Team"]
12
+ s.date = "2012-10-30"
13
+ s.description = "A Rails 3.1 & 3.2 version of Encrypted Cookie Store by FooBarWidget"
14
+ s.email = "scottwb@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".rspec",
21
+ ".travis.yml",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/encrypted_cookie_store.rb",
29
+ "lib/encrypted_cookie_store/constants.rb",
30
+ "lib/encrypted_cookie_store/encrypted_cookie_store.rb",
31
+ "lib/encrypted_cookie_store/railtie.rb",
32
+ "lib/tasks/encrypted_cookie_store.rake",
33
+ "spec/encrypted_cookie_store_spec.rb",
34
+ "validas-encrypted_cookie_store.gemspec"
35
+ ]
36
+ s.homepage = "https://github.com/validas/encrypted_cookie_store"
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = "1.8.24"
39
+ s.summary = "A Rails 3.1 & 3.2 version of Encrypted Cookie Store by FooBarWidget"
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<rails>, ["~> 3.2.0"])
46
+ s.add_development_dependency(%q<rake>, [">= 0"])
47
+ s.add_development_dependency(%q<bundler>, [">= 0"])
48
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
49
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<rails>, ["~> 3.2.0"])
52
+ s.add_dependency(%q<rake>, [">= 0"])
53
+ s.add_dependency(%q<bundler>, [">= 0"])
54
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
55
+ s.add_dependency(%q<jeweler>, [">= 0"])
56
+ end
57
+ else
58
+ s.add_dependency(%q<rails>, ["~> 3.2.0"])
59
+ s.add_dependency(%q<rake>, [">= 0"])
60
+ s.add_dependency(%q<bundler>, [">= 0"])
61
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
62
+ s.add_dependency(%q<jeweler>, [">= 0"])
63
+ end
64
+ end
65
+
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: validas-encrypted_cookie_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - FooBarWidget
9
+ - Scott W. Bradley
10
+ - Validas Engineering Team
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-10-30 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rails
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 3.2.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ version: 3.2.0
32
+ - !ruby/object:Gem::Dependency
33
+ name: rake
34
+ requirement: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: bundler
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ - !ruby/object:Gem::Dependency
65
+ name: rspec
66
+ requirement: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ~>
70
+ - !ruby/object:Gem::Version
71
+ version: 2.6.0
72
+ type: :development
73
+ prerelease: false
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: 2.6.0
80
+ - !ruby/object:Gem::Dependency
81
+ name: jeweler
82
+ requirement: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ description: A Rails 3.1 & 3.2 version of Encrypted Cookie Store by FooBarWidget
97
+ email: scottwb@gmail.com
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files:
101
+ - LICENSE.txt
102
+ - README.md
103
+ files:
104
+ - .rspec
105
+ - .travis.yml
106
+ - Gemfile
107
+ - Gemfile.lock
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - VERSION
112
+ - lib/encrypted_cookie_store.rb
113
+ - lib/encrypted_cookie_store/constants.rb
114
+ - lib/encrypted_cookie_store/encrypted_cookie_store.rb
115
+ - lib/encrypted_cookie_store/railtie.rb
116
+ - lib/tasks/encrypted_cookie_store.rake
117
+ - spec/encrypted_cookie_store_spec.rb
118
+ - validas-encrypted_cookie_store.gemspec
119
+ homepage: https://github.com/validas/encrypted_cookie_store
120
+ licenses: []
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 1.8.24
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: A Rails 3.1 & 3.2 version of Encrypted Cookie Store by FooBarWidget
143
+ test_files: []