encrypted-cookies 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,60 @@
1
+ = Encrypted Cookies
2
+
3
+ == Description
4
+
5
+ Encrypted cookie jar for Rails 3.
6
+
7
+ == Summary
8
+
9
+ There are times when one must store things in a cookie that are not necessarily meant for anyone's eyes. It is probably not the best practice to do so, but when one must, it's better to do so with at least a minimal bit of security. +encrypted-cookies+ will encrypt and sign the contents of the cookie before writing it back to the browser and then decrypt and verify the data on each subsequent request.
10
+
11
+ EncryptedCookieJar is very much like the SignedCookieJar. The difference is it uses ActiveSupport::MessageEncryptor instead of ActiveSupport::MessageVerifier to generate the value set on the cookie. Fairly straight forward.
12
+
13
+ == Usage
14
+
15
+ Write an encrypted cookie:
16
+
17
+ cookies.encrypted[:encrypted_cookie] = "You don't know what this says."
18
+
19
+ Read the encrypted cookie:
20
+
21
+ cookies.encrypted[:encrypted_cookie] # => You don't know what this says.
22
+
23
+ You can chain the encrypted cookie jar:
24
+
25
+ cookies.permanent.encrypted[:permanent_encrypted_cookie] = "You don't know what this says, and it will be here for 20 years..."
26
+ cookies.encrypted[:permanent_encrypted_cookie] # => "You don't know what this says, and it will be here for 20 years..."
27
+
28
+ == Requirements
29
+
30
+ Encrypted cookies works with Rails 3.0.0, but because of a requirement bug in ActionPack 3.0.4, the tests only run with version 3.0.5.
31
+
32
+ For more information:
33
+ https://rails.lighthouseapp.com/projects/8994/tickets/6393-action_dispatchhttprequestrb-missing-a-require
34
+
35
+ == Disclaimer
36
+
37
+ This is provided as is. No guarantee is given for the security of the data written or read by this software. This has not been tested for cryptographic rigor. Use at your own discretion and risk. This should not be only level of security you use for your data. It uses ActiveSupport::MessageEncryptor to encrypt and ActiveSupport::MessageVerifier to sign the cookie values, so it is at best as secure as these two libraries. Be sure to keep your AppName::Application.config.secret_token safe and secret, as both of the above libraries use it in your Rails application.
38
+
39
+ == License
40
+
41
+ Copyright (c) 2011 Les Fletcher
42
+
43
+ Permission is hereby granted, free of charge, to any person obtaining
44
+ a copy of this software and associated documentation files (the
45
+ "Software"), to deal in the Software without restriction, including
46
+ without limitation the rights to use, copy, modify, merge, publish,
47
+ distribute, sublicense, and/or sell copies of the Software, and to
48
+ permit persons to whom the Software is furnished to do so, subject to
49
+ the following conditions:
50
+
51
+ The above copyright notice and this permission notice shall be
52
+ included in all copies or substantial portions of the Software.
53
+
54
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
55
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
56
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
57
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
58
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
59
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
60
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ desc "Run all tests"
5
+ task :test do
6
+ sh "ruby test/encryped_cookie_test.rb"
7
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "encrypted-cookies/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "encrypted-cookies"
7
+ s.version = EncryptedCookies::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Les Fletcher"]
10
+ s.email = ["les.fletcher@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Encrypted cookies for Rails 3}
13
+ s.description = %q{Add an encrypted cookie jar for Rails 3 that can be chained with permanent and signed cookies}
14
+
15
+ s.rubyforge_project = "encrypted-cookies"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency('test-unit', '~> 2.2.0')
23
+ s.add_dependency('activesupport', '~> 3.0.0')
24
+ s.add_dependency('actionpack', '~> 3.0.0')
25
+
26
+ # needs rails 3.0.5 because of an actionpack requirement bug in 3.0.4
27
+ # https://rails.lighthouseapp.com/projects/8994/tickets/6393-action_dispatchhttprequestrb-missing-a-require
28
+ end
@@ -0,0 +1,2 @@
1
+ require File.expand_path( File.join( File.dirname( __FILE__ ), 'encrypted-cookies', 'cookie_jar' ) )
2
+ require File.expand_path( File.join( File.dirname( __FILE__ ), 'encrypted-cookies', 'encrypted_cookie_jar' ) )
@@ -0,0 +1,24 @@
1
+ module EncryptedCookies
2
+ module CookieJar
3
+ # Returns a jar that'll automatically set the assigned cookies to the top level domain, regardless of if there
4
+ # is a subdomain present or not. Example:
5
+ #
6
+ # cookies.encypted[:encrypted_cookie] = "you don't know what this says"
7
+ # # => Set-Cookie: LSuus8pkXd...ckqiG6qGlwuhSQn--4Eb16w1z7ouNXQZAxV5Bjw==; path=/; expires=Sun, 27-Mar-2011 03:24:16 GMT
8
+ #
9
+ # This jar allows chaining with other jars as well, so you can set tld, signed cookies. Examples:
10
+ #
11
+ # cookies.permanent.encypted[:encrypted_permanent] = "you don't know what this says, but it will be here for 20 years"
12
+ # # => Set-Cookie: Sok2G6hGs...XFeUpDWQLT8=--UZe+JlZPlMuxHYSq09oV0w==; path=/; expires=Thu, 27 Mar 2031 13:48:43 GMT
13
+ #
14
+ # To read encypted cookies:
15
+ #
16
+ # cookies.encrypted[:encrypted_cookie] # => "you don't know what this says"
17
+ # cookies.encrypted[:encrypted_permanent] # => "you don't know what this says, but it will be here for 20 years"
18
+ def encrypted
19
+ @encrypted ||= EncryptedCookieJar.new(self, @secret)
20
+ end
21
+ end
22
+ end
23
+
24
+ ActionDispatch::Cookies::CookieJar.send(:include, EncryptedCookies::CookieJar)
@@ -0,0 +1,61 @@
1
+ module EncryptedCookies
2
+ class EncryptedCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
3
+ MAX_COOKIE_SIZE = 4096 # Cookies can typically store 4096 bytes.
4
+ SECRET_MIN_LENGTH = 30 # Characters
5
+
6
+ def initialize(parent_jar, secret)
7
+ ensure_secret_secure(secret)
8
+ @parent_jar = parent_jar
9
+ @encrypter = ActiveSupport::MessageEncryptor.new(secret)
10
+ end
11
+
12
+ def [](name)
13
+ if encrypted_message = @parent_jar[name]
14
+ @encrypter.decrypt(encrypted_message)
15
+ end
16
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
17
+ nil
18
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
19
+ nil
20
+ end
21
+
22
+ def []=(key, options)
23
+ if options.is_a?(Hash)
24
+ options.symbolize_keys!
25
+ options[:value] = @encrypter.encrypt(options[:value])
26
+ else
27
+ options = { :value => @encrypter.encrypt(options) }
28
+ end
29
+
30
+ raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
31
+ @parent_jar[key] = options
32
+ end
33
+
34
+ def method_missing(method, *arguments, &block)
35
+ @parent_jar.send(method, *arguments, &block)
36
+ end
37
+
38
+ protected
39
+
40
+ # To prevent users from using something insecure like "Password" we make sure that the
41
+ # secret they've provided is at least 30 characters in length.
42
+ def ensure_secret_secure(secret)
43
+ if secret.blank?
44
+ raise ArgumentError, "A secret is required to generate an " +
45
+ "integrity hash for cookie session data. Use " +
46
+ "config.secret_token = \"some secret phrase of at " +
47
+ "least #{SECRET_MIN_LENGTH} characters\"" +
48
+ "in config/initializers/secret_token.rb"
49
+ end
50
+
51
+ if secret.length < SECRET_MIN_LENGTH
52
+ raise ArgumentError, "Secret should be something secure, " +
53
+ "like \"#{ActiveSupport::SecureRandom.hex(16)}\". The value you " +
54
+ "provided, \"#{secret}\", is shorter than the minimum length " +
55
+ "of #{SECRET_MIN_LENGTH} characters"
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ ActionDispatch::Cookies.send(:include, EncryptedCookies)
@@ -0,0 +1,3 @@
1
+ module EncryptedCookies
2
+ VERSION = "0.2"
3
+ end
@@ -0,0 +1,75 @@
1
+ require File.expand_path( File.join( File.dirname( __FILE__ ), 'test_helper' ) )
2
+
3
+ # so that we can get access to the encrypted values and mess with them for testing
4
+ module EncryptedCookies
5
+ class EncryptedCookieJar
6
+ def encrypted_value(name)
7
+ @parent_jar[name]
8
+ end
9
+
10
+ def set_encrypted_value(name, value)
11
+ @parent_jar[name] = value
12
+ end
13
+
14
+ end
15
+ end
16
+
17
+ class TestEncryptedCookies < Test::Unit::TestCase
18
+
19
+ GOOD_SECRET_1 = "200b85f78a0ac1add6494111d899107df8c25f72b13ec7480f906f4cb8bef32cc1e7b4c0d31f57493ff062cdd9bf37d41636fdfb453af7c7c73f598b257d3c89"
20
+ GOOD_SECRET_2 = "8127a90b3352b34459a1649da6f3a01358632c06a7ded98e4633fcfc8d32a7508d8e87a1db1b826330f090f4518098860dd20c10604df9b1b577e1b39268deb6"
21
+ BAD_SECRET = "iamtooshort"
22
+
23
+ def setup
24
+ @cookie_jar = ActionDispatch::Cookies::CookieJar.new
25
+ @encrypted_cookie_jar = EncryptedCookies::EncryptedCookieJar.new(@cookie_jar, GOOD_SECRET_1)
26
+ @str = "test string"
27
+ end
28
+
29
+ def test_secret
30
+ assert_raise (ArgumentError) { EncryptedCookies::EncryptedCookieJar.new(@cookie_jar, nil) }
31
+ assert_raise (ArgumentError) { EncryptedCookies::EncryptedCookieJar.new(@cookie_jar, "") }
32
+ assert_raise (ArgumentError) { EncryptedCookies::EncryptedCookieJar.new(@cookie_jar, BAD_SECRET) }
33
+ assert_nothing_raised { EncryptedCookies::EncryptedCookieJar.new(@cookie_jar, GOOD_SECRET_1) }
34
+ end
35
+
36
+ def test_basic_encryption_decryption
37
+ @encrypted_cookie_jar[:test] = @str
38
+
39
+ assert @encrypted_cookie_jar.encrypted_value(:test) != @str
40
+ assert @encrypted_cookie_jar[:test] == @str
41
+ end
42
+
43
+ def test_tampered_signature
44
+ @encrypted_cookie_jar[:test] = @str
45
+ enc_value = @encrypted_cookie_jar.encrypted_value(:test)
46
+
47
+ data, digest = enc_value.split("--")
48
+ @encrypted_cookie_jar.set_encrypted_value(:test, "#{data}--sdgsad")
49
+
50
+ assert @encrypted_cookie_jar[:test].nil?
51
+ end
52
+
53
+ # a different encrypted cookie jar with a different secret can't read another's values
54
+ def test_another_cookie_jar
55
+ @encrypted_cookie_jar[:test] = @str
56
+ enc_value = @encrypted_cookie_jar.encrypted_value(:test)
57
+
58
+ encrypted_cookie_jar_2 = EncryptedCookies::EncryptedCookieJar.new(@cookie_jar, GOOD_SECRET_2)
59
+ encrypted_cookie_jar_2.set_encrypted_value(:test, enc_value)
60
+
61
+ assert encrypted_cookie_jar_2[:test].nil?
62
+ end
63
+
64
+ # a different encrypted jar with the same secret can decode the value
65
+ def test_same_cookie_jar
66
+ @encrypted_cookie_jar[:test] = @str
67
+ enc_value = @encrypted_cookie_jar.encrypted_value(:test)
68
+
69
+ encrypted_cookie_jar_2 = EncryptedCookies::EncryptedCookieJar.new(@cookie_jar, GOOD_SECRET_1)
70
+ encrypted_cookie_jar_2.set_encrypted_value(:test, enc_value)
71
+
72
+ assert encrypted_cookie_jar_2[:test] == @str
73
+ end
74
+
75
+ end
@@ -0,0 +1,6 @@
1
+ require 'test/unit'
2
+
3
+ require 'active_support'
4
+ require 'action_dispatch'
5
+
6
+ require File.expand_path( File.join( File.dirname( __FILE__ ), '..', 'lib', 'encrypted-cookies' ) )
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: encrypted-cookies
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: "0.2"
6
+ platform: ruby
7
+ authors:
8
+ - Les Fletcher
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-03 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: test-unit
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 2.2.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 3.0.0
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: actionpack
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 3.0.0
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ description: Add an encrypted cookie jar for Rails 3 that can be chained with permanent and signed cookies
50
+ email:
51
+ - les.fletcher@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - Gemfile
61
+ - README.rdoc
62
+ - Rakefile
63
+ - encrypted-cookies.gemspec
64
+ - lib/encrypted-cookies.rb
65
+ - lib/encrypted-cookies/cookie_jar.rb
66
+ - lib/encrypted-cookies/encrypted_cookie_jar.rb
67
+ - lib/encrypted-cookies/version.rb
68
+ - test/encryped_cookie_test.rb
69
+ - test/test_helper.rb
70
+ has_rdoc: true
71
+ homepage: ""
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options: []
76
+
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ requirements: []
92
+
93
+ rubyforge_project: encrypted-cookies
94
+ rubygems_version: 1.5.0
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: Encrypted cookies for Rails 3
98
+ test_files:
99
+ - test/encryped_cookie_test.rb
100
+ - test/test_helper.rb