safe_cookies 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- M2M1NGJkNGIyYmRhNmQ5OWZmYjU2MTVmMTEwZTc3NzRjNzRlNzE5Zg==
4
+ Njc0YjdlMzZlMTRmNjk3NGQ1NWQ5NDU0YjU0ZGQwMTA0OGYyM2Y1MQ==
5
5
  data.tar.gz: !binary |-
6
- MTNiNGMwMjcyMzBkMmQzYzhmODVkNWIzYzg0N2FiNGZmNWE0ZTFkNQ==
6
+ YzQ4OWE2ODk3NWU4M2U0OTQ3MjRlZDdiYzIzM2MyNjFjNmE0YzRmOQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YjAyZWNhNTExNWFmZjk3MzAzYmVmYmY1NDAwZDQ4YzA1YWJkOTk0NTAzMDc0
10
- OWE3YTM5YmZiMTYzMDg0ZmRiZjFhZGNiMTE5OWU3MWNjZTQ4MGRkNTNlYWNk
11
- ZDhlYmM1Y2RjNWE1ZmRlYTY5OGJlYmIxZjJmMTUzODc5NzE1OTI=
9
+ NmJjNjgzM2VjZTdmY2E4ZmM3OTQwOTEwNzgwNTQ1YTMxOTZlOWY4NDA3Nzgy
10
+ YTJlZmRkOTQyMjQwNTE1NWUxN2I2M2VmNTNjMjMyZTk5NGQ0MTlmY2M0MWMz
11
+ NmQwM2EwZmE4M2I4MjJlNmVjZDlmZmJmNWY0NDYxN2Q3ZWI2YmE=
12
12
  data.tar.gz: !binary |-
13
- NThmZjk3M2M5ZGY5YmZiMmI5ZjRkMzA3MWUyOTRlZWMzMmEwY2VjZTRkMWFj
14
- OWZjZDk5OGJlMDM0MGE2MDQ0NmM4NTgyMDA4Y2VjYzY5NzEyNjJjZjMzOTE3
15
- YWEzYjlmNzkwZjQyZmRhZWI5YzhmNzcyZjlmMmE3NTIxZTRjODE=
13
+ YjU4NDU0NjUwNjgzZGNhYzNjNTBiM2JjOTQyMGZlZDY3YThjYjRiYTUzZDE5
14
+ ZDU5NjVhOTNlYjM1NWE2NDUwMjI1YzZmZmU5OGZhZmI3M2I2ZjM5YzMyNzNl
15
+ YWZjYWFjMGIwMWU5YTg2NThlMzJkNjExMDdjODgyMzhkY2ExYmY=
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SafeCookies
2
2
 
3
- This Gem brings a middleware that will make all cookies secure. In detail, it will:
3
+ This Gem has a middleware that will make all cookies secure. In detail, it will:
4
4
 
5
5
  * set all new application cookies 'HttpOnly', unless specified otherwise
6
6
  * set all new application cookies 'secure', if the request came via HTTPS and not specified otherwise
@@ -13,14 +13,14 @@ Add this line to your application's Gemfile:
13
13
 
14
14
  gem 'safe_cookies'
15
15
 
16
- Then run `bundle`.
16
+ Then run `bundle install`.
17
17
 
18
- Though this gem is aimed at Rails applications, you may even use it without Rails. Install it then with
19
- `gem install safe_cookies`.
18
+ Though this gem is aimed at Rails applications, you may even use it without
19
+ Rails. In that case, install it with `gem install safe_cookies`.
20
20
 
21
21
 
22
22
  ### Step 2
23
- **Rails 3**: add the following line in config/application.rb:
23
+ **Rails 3 and 4**: add the following line in config/application.rb:
24
24
 
25
25
  class Application < Rails::Application
26
26
  # ...
@@ -36,8 +36,8 @@ Though this gem is aimed at Rails applications, you may even use it without Rail
36
36
  end
37
37
 
38
38
  ### Step 3
39
- Register cookies, either just after the lines you added in step 1 or in in an initializer
40
- (e.g. in `config/initializers/safe_cookies.rb):
39
+ Register cookies, either just after the lines you added above or in in an initializer
40
+ (e.g. in `config/initializers/safe_cookies.rb`):
41
41
 
42
42
  SafeCookies.configure do |config|
43
43
  config.register_cookie :remember_token, :expire_after => 1.year
@@ -46,28 +46,33 @@ Register cookies, either just after the lines you added in step 1 or in in an in
46
46
  config.register_cookie :javascript_data, :expire_after => 1.day, :http_only => false
47
47
  end
48
48
 
49
- This will have the `default_language` cookie not made secure, the `javascript_data` cookie
50
- not made http-only. It will rewrite the `remember_token` with an expiry of one year and the
51
- `last_action` cookie with an expiry of 30 days, making both of them secure and http-only.
49
+ If a request has any of those four cookies, the middleware will set them anew. The `remember_token` and
50
+ `last_action` cookies will be made `secure` and `HttpOnly`.
51
+ Since we want to access the default language even if the user comes via HTTP, the `default_language`
52
+ cookie is not made secure. Analogous, the `javascript_data` cookie will be used by a script and hence is
53
+ not made `HttpOnly`.
54
+
52
55
  Available options are: `:expire_after (required), :path, :secure, :http_only`.
53
56
 
54
- ### Step 4 (only for Rails 2)
55
- Override `SafeCookies::Middleware#handle_unknown_cookies(cookies)` (see "Dealing with unregistered
56
- cookies" below).
57
+ ### Step 4 (important for Rails 2 only)
58
+ Override `SafeCookies::Middleware#handle_unknown_cookies(cookies)` to notify you
59
+ e.g. by email (see "Dealing with unregistered cookies" below).
57
60
 
58
61
 
59
62
  ## Dealing with unregistered cookies
60
63
 
61
- The middleware is not able to secure cookies without knowing their attributes (most important: their
62
- expiry). Unfortunately, [the client won't ever tell us](http://tools.ietf.org/html/rfc6265#section-4.2.2)
63
- if it stores the cookie with flags such as "secure" or which expiry date it currently has.
64
- Therefore, it is important to register all cookies that users may come with, specifying their properties.
65
- Unregistered cookies cannot be secured.
66
-
67
- If a request brings a cookie that is not registered, the middleware will raise a
68
- `SafeCookies::UnknownCookieError`. Rails 3+ should handle the exception as any other in your application,
69
- but by default, **you will not be notified from Rails 2 applications** and the user will see a standard
70
- 500 Server Error. Override `SafeCookies::Middleware#handle_unknown_cookies(cookies)` in the config
64
+ The middleware is not able to secure cookies without knowing their attributes
65
+ (most importantly: their expiry). Unfortunately, [the client won't ever tell us](http://tools.ietf.org/html/rfc6265#section-4.2.2)
66
+ if it stores the cookie with flags such as "secure" or which expiry date it
67
+ currently has. Therefore, it is important to register all cookies that may be
68
+ sent by the client, specifying their properties. Unregistered cookies cannot be
69
+ secured.
70
+
71
+ If a request contains a cookie that is not registered, the middleware will raise
72
+ a `SafeCookies::UnknownCookieError`. Rails 3+ should handle the exception as any
73
+ other in your application, but by default, **you will not be notified from Rails
74
+ 2 applications** and the user will see a standard 500 Server Error. Override
75
+ `SafeCookies::Middleware#handle_unknown_cookies(cookies)` in the config
71
76
  initializer for customized exception handling (like, notifying you per email).
72
77
 
73
78
  You should register any cookie that your application has to do with. However, there are cookies that you
@@ -87,3 +92,15 @@ The configuration option `config.fix_paths` turns on fixing this error. It requi
87
92
  point of time from which cookies will be secured with the correct path. The middleware will fix the cookie
88
93
  paths by rewriting all cookies that it has already secured, but only if the were secured before the time
89
94
  you specified.
95
+
96
+
97
+ ## Development
98
+
99
+ - Tests live in `spec`.
100
+ - You can run specs from the project root by saying `bundle exec rake`.
101
+
102
+ If you would like to contribute:
103
+
104
+ - Fork the repository.
105
+ - Push your changes **with passing specs**.
106
+ - Send us a pull request.
@@ -6,15 +6,14 @@ module SafeCookies
6
6
  # (see below), leading to multiple cookies per domain.
7
7
  #
8
8
  # If the cookies were secured before the configured datetime, this method
9
- # instructs the client to delete all cookies it sent with the request + the
10
- # SECURED_COOKIE_NAME helper cookie.
9
+ # instructs the client to delete all cookies it sent with the request and
10
+ # that we are able to rewrite, plus the SECURED_COOKIE_NAME helper cookie.
11
+ #
11
12
  # The middleware still sees the request cookies and will rewrite them as
12
13
  # if it hadn't seen them before, setting them on the correct path (root,
13
- # per default).
14
+ # by default).
14
15
  def delete_cookies_on_bad_path
15
- registered_cookies_in_request.keys.each do |registered_cookie|
16
- delete_cookie_for_current_directory(registered_cookie)
17
- end
16
+ rewritable_request_cookies.keys.each &method(:delete_cookie_for_current_directory)
18
17
  delete_cookie_for_current_directory(SafeCookies::SECURED_COOKIE_NAME)
19
18
 
20
19
  # Delete this cookie here, so the middleware believes it hasn't secured
@@ -25,17 +24,15 @@ module SafeCookies
25
24
  private
26
25
 
27
26
  def fix_cookie_paths?
28
- @configuration.fix_cookie_paths or return false
29
-
30
- cookies_need_path_fix = (secured_old_cookies_timestamp < @configuration.correct_cookie_paths_timestamp)
31
-
32
- cookies_have_been_rewritten_before? and cookies_need_path_fix
27
+ @config.fix_cookie_paths &&
28
+ cookies_have_been_rewritten_before? &&
29
+ (secured_old_cookies_timestamp < @config.correct_cookie_paths_timestamp)
33
30
  end
34
31
 
35
32
  # Delete cookies by giving them an expiry in the past,
36
33
  # cf. https://tools.ietf.org/html/rfc6265#section-4.1.2.
37
34
  #
38
- # Most important, as specified in
35
+ # Most importantly, as specified in
39
36
  # https://tools.ietf.org/html/rfc6265#section-4.1.2.4 and in section 5.1.4,
40
37
  # cookies set without a path will be set for the current "directory", that is:
41
38
  #
@@ -57,12 +54,18 @@ module SafeCookies
57
54
  end
58
55
 
59
56
  def secured_old_cookies_timestamp
60
- Time.rfc2822(request_cookies[SafeCookies::SECURED_COOKIE_NAME])
57
+ @request.cookies.has_key?(SafeCookies::SECURED_COOKIE_NAME) or return nil
58
+
59
+ Time.rfc2822(@request.cookies[SafeCookies::SECURED_COOKIE_NAME])
61
60
  rescue ArgumentError
62
61
  # If we cannot parse the secured_old_cookies time,
63
62
  # assume it was before we noticed the bug to ensure
64
63
  # broken cookie paths will be fixed.
65
- Time.parse "2013-08-25 0:00"
64
+ #
65
+ # One reason to get here is that Rack::Utils.rfc2822 produces an invalid
66
+ # datetime string in Rack v1.1, writing the date with dashes
67
+ # (e.g. '04-Nov-2013').
68
+ Time.parse "2013-08-25 00:00"
66
69
  end
67
70
 
68
71
  end
@@ -3,6 +3,9 @@ module SafeCookies
3
3
 
4
4
  KNOWN_COOKIES_DIVIDER = '|'
5
5
 
6
+ # Since we have to operate on and modify the actual @headers hash that the
7
+ # application returns, cache the @headers['Set-Cookie'] string so that
8
+ # later on, we still know what the application did set.
6
9
  def cache_application_cookies_string
7
10
  cookies = @headers['Set-Cookie']
8
11
  # Rack 1.1 returns an Array
@@ -41,31 +44,31 @@ module SafeCookies
41
44
  options[:secure] = should_be_secure?(name)
42
45
  options[:httponly] = should_be_http_only?(name)
43
46
 
47
+ # Rack magic
44
48
  Rack::Utils.set_cookie_header!(@headers, name, options)
45
49
  end
46
50
 
47
51
 
48
52
  # getters
53
+
54
+ # returns the request cookies minus ignored cookies
55
+ def request_cookies
56
+ Util.except!(@request.cookies.dup, *@config.ignored_cookies)
57
+ end
49
58
 
50
59
  def stored_application_cookie_names
51
60
  store_cookie = @request.cookies[STORE_COOKIE_NAME] || ""
52
61
  store_cookie.split(KNOWN_COOKIES_DIVIDER)
53
62
  end
54
63
 
55
- # returns those of the registered cookies that appear in the request
56
- def registered_cookies_in_request
57
- Util.slice(@configuration.registered_cookies, *request_cookies.keys)
64
+ def rewritable_request_cookies
65
+ Util.slice(request_cookies, *@config.registered_cookies.keys)
58
66
  end
59
67
 
60
68
  def known_cookie_names
61
69
  known = [STORE_COOKIE_NAME, SECURED_COOKIE_NAME]
62
70
  known += stored_application_cookie_names
63
- known += @configuration.registered_cookies.keys
64
- end
65
-
66
- # returns the request cookies minus ignored cookies
67
- def request_cookies
68
- Util.except!(@request.cookies.dup, *@configuration.ignored_cookies)
71
+ known += @config.registered_cookies.keys
69
72
  end
70
73
 
71
74
 
@@ -77,7 +80,7 @@ module SafeCookies
77
80
 
78
81
  def should_be_secure?(cookie)
79
82
  cookie_name = cookie.split('=').first.strip
80
- ssl? and not @configuration.insecure_cookie?(cookie_name)
83
+ ssl? and not @config.insecure_cookie?(cookie_name)
81
84
  end
82
85
 
83
86
  def ssl?
@@ -91,7 +94,7 @@ module SafeCookies
91
94
 
92
95
  def should_be_http_only?(cookie)
93
96
  cookie_name = cookie.split('=').first.strip
94
- not @configuration.scriptable_cookie?(cookie_name)
97
+ not @config.scriptable_cookie?(cookie_name)
95
98
  end
96
99
 
97
100
  end
@@ -1,3 +1,3 @@
1
1
  module SafeCookies
2
- VERSION = '0.1.6'
2
+ VERSION = '0.1.7'
3
3
  end
data/lib/safe_cookies.rb CHANGED
@@ -5,6 +5,7 @@ require "safe_cookies/helpers"
5
5
  require "safe_cookies/util"
6
6
  require "safe_cookies/version"
7
7
  require "rack"
8
+ require "time" # add Time#rfc2822
8
9
 
9
10
  # Naming:
10
11
  # - application_cookies: cookies received from the application. The 'Set-Cookie' header is a string
@@ -28,16 +29,16 @@ module SafeCookies
28
29
 
29
30
  def initialize(app)
30
31
  @app = app
31
- @configuration = SafeCookies.configuration or raise "Don't know what to do without configuration"
32
+ @config = SafeCookies.configuration or raise "Don't know what to do without configuration"
32
33
  end
33
34
 
34
35
  def call(env)
35
36
  reset_instance_variables
36
37
 
37
38
  @request = Rack::Request.new(env)
38
- ensure_no_unknown_cookies_in_request!
39
+ check_if_request_has_unknown_cookies
39
40
 
40
- # calling the next middleware
41
+ # call the next middleware up the stack
41
42
  status, @headers, body = @app.call(env)
42
43
  cache_application_cookies_string
43
44
 
@@ -52,13 +53,14 @@ module SafeCookies
52
53
 
53
54
  private
54
55
 
56
+ # Instance variables survive requests because the middleware is a singleton.
55
57
  def reset_instance_variables
56
58
  @request, @headers, @application_cookies_string = nil
57
59
  end
58
60
 
59
- # Make sure we get notified if a client comes with an unregistered cookie,
60
- # because we do not want any cookie not to be secured.
61
- def ensure_no_unknown_cookies_in_request!
61
+ # Do something if a request has an unregistered cookie, because we do not
62
+ # want any cookie to not be secured. By default, we raise an error.
63
+ def check_if_request_has_unknown_cookies
62
64
  request_cookie_names = request_cookies.keys.map(&:to_s)
63
65
  unknown_cookie_names = request_cookie_names - known_cookie_names
64
66
 
@@ -67,7 +69,7 @@ module SafeCookies
67
69
  end
68
70
  end
69
71
 
70
- # Overwrites @header['Set-Cookie']
72
+ # Overwrites @header['Set-Cookie']!
71
73
  def enhance_application_cookies!
72
74
  if @application_cookies_string
73
75
  cookies = @application_cookies_string.split("\n")
@@ -90,8 +92,8 @@ module SafeCookies
90
92
  end
91
93
  end
92
94
 
93
- # Store the names of cookies that are set by the application. We are already
94
- # securing those and therefore do not need to rewrite them.
95
+ # Store the names of cookies that are set by the application.
96
+ # (We are already securing those and will not need to rewrite them.)
95
97
  def store_application_cookie_names
96
98
  if @application_cookies_string
97
99
  application_cookie_names = stored_application_cookie_names + @application_cookies_string.scan(COOKIE_NAME_REGEX)
@@ -107,23 +109,21 @@ module SafeCookies
107
109
  # With the SECURED_COOKIE_NAME cookie we remember the exact time that we
108
110
  # rewrote the cookies.
109
111
  def rewrite_request_cookies
110
- cookies_to_rewrite = request_cookies || []
112
+ rewritable_cookies = rewritable_request_cookies
111
113
 
112
114
  # don't rewrite request cookies that the application is setting in the response
113
115
  if @application_cookies_string
114
- application_cookie_names = @application_cookies_string.scan(COOKIE_NAME_REGEX)
115
- Util.except!(cookies_to_rewrite, *application_cookie_names)
116
+ app_cookie_names = @application_cookies_string.scan(COOKIE_NAME_REGEX)
117
+ Util.except!(rewritable_cookies, *app_cookie_names)
116
118
  end
117
119
 
118
- if cookies_to_rewrite.any?
119
- registered_cookies_in_request.each do |cookie_name, options|
120
- value = request_cookies[cookie_name]
121
-
120
+ if rewritable_cookies.any?
121
+ rewritable_cookies.each do |cookie_name, value|
122
+ options = @config.registered_cookies[cookie_name]
122
123
  set_cookie!(cookie_name, value, options)
123
124
  end
124
125
 
125
- formatted_now = Rack::Utils.rfc2822(Time.now.gmtime)
126
- set_cookie!(SECURED_COOKIE_NAME, formatted_now, :expire_after => HELPER_COOKIES_LIFETIME)
126
+ set_cookie!(SECURED_COOKIE_NAME, Time.now.gmtime.rfc2822, :expire_after => HELPER_COOKIES_LIFETIME)
127
127
  end
128
128
  end
129
129
 
@@ -3,7 +3,7 @@ require 'cgi'
3
3
 
4
4
  describe SafeCookies::Middleware do
5
5
 
6
- describe 'cookie path fix' do
6
+ describe 'cookie path fix,' do
7
7
 
8
8
  subject { described_class.new(app) }
9
9
  let(:app) { stub 'application' }
@@ -15,12 +15,11 @@ describe SafeCookies::Middleware do
15
15
  end
16
16
 
17
17
  def set_default_request_cookies(secured_at = Time.parse('2040-01-01 00:00'))
18
- secured_old_cookies_time = Rack::Utils.rfc2822(secured_at.gmtime)
19
- set_request_cookies(env, 'cookie_to_update=some_data', "secured_old_cookies=#{CGI::escape(secured_old_cookies_time)}")
18
+ set_request_cookies(env, 'cookie_to_update=some_data', "secured_old_cookies=#{CGI::escape(secured_at.gmtime.rfc2822)}")
20
19
  end
21
20
 
22
21
 
23
- context 'rewriting previously secured cookies' do
22
+ context 'rewriting previously secured cookies,' do
24
23
 
25
24
  before do
26
25
  SafeCookies.configure do |config|
@@ -109,11 +108,21 @@ describe SafeCookies::Middleware do
109
108
  headers['Set-Cookie'].should =~ %r(secured_old_cookies=\w+.*path=/;)
110
109
  end
111
110
 
112
- it 'rewrites cookies even if it cannot parse the secured_old_cookies timestamp' do
113
- set_request_cookies(env, 'cookie_to_update=some_data', 'secured_old_cookies=rubbish')
114
-
115
- code, headers, response = subject.call(env)
116
- headers['Set-Cookie'].should =~ /cookie_to_update=some_data;.*path=\/;/
111
+ context 'unparseable secured_old_cookies timestamp,' do
112
+
113
+ before do
114
+ set_request_cookies(env, 'cookie_to_update=some_data', 'secured_old_cookies=rubbish')
115
+ code, @headers, response = subject.call(env)
116
+ end
117
+
118
+ it 'rewrites cookies anyway' do
119
+ @headers['Set-Cookie'].should include('cookie_to_update=some_data;')
120
+ end
121
+
122
+ it 'sets a new, parseable secured_old_cookies timestamp' do
123
+ @headers['Set-Cookie'].should include("secured_old_cookies=#{CGI::escape @now.gmtime.rfc2822}")
124
+ end
125
+
117
126
  end
118
127
 
119
128
  end
@@ -96,7 +96,7 @@ describe SafeCookies::Middleware do
96
96
  env['PATH_INFO'] = '/special/path/subfolder'
97
97
 
98
98
  code, headers, response = subject.call(env)
99
- expected_expiry = Rack::Utils.rfc2822((Time.now + 3600).gmtime) # a special date format needed here
99
+ expected_expiry = (Time.now + 3600).gmtime.rfc2822 # a special date format needed here
100
100
  headers['Set-Cookie'].should =~ %r(foo=bar; path=/special/path; expires=#{expected_expiry}; secure; HttpOnly)
101
101
  end
102
102
 
@@ -208,7 +208,7 @@ describe SafeCookies::Middleware do
208
208
 
209
209
  end
210
210
 
211
- context 'unknown request cookies' do
211
+ context 'when a request has unknown cookies,' do
212
212
 
213
213
  it 'raises an error if there is an unknown cookie' do
214
214
  set_request_cookies(env, 'foo=bar')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safe_cookies
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Schöler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-21 00:00:00.000000000 Z
11
+ date: 2013-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack