frikandel 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d21550e8f406f5cfadd7246e3a5b511e6719b2d
4
- data.tar.gz: c205899f482cc2e2e6fc64f69070c7b5f4cdda59
3
+ metadata.gz: c72cb658721f8dedcdc8981612407748dcd1c3bb
4
+ data.tar.gz: ce5f11294df1af510cf1c8d95b948217e81b98a7
5
5
  SHA512:
6
- metadata.gz: 40977fc969914d45e38951d1bcd37577bb8376f8e8c9e13dbf56d2076194973409de535136ee6af30e3c31b5ab34f52b1350e630cc6723c179b0c131f8288b0b
7
- data.tar.gz: 62eb2c7883e57b57c297db621bc5eccf42ab4f653a93ff7e7867fa7bc57d386c7765fd11914cdb5f954085c1aea8842d1a27dc33346598b30ebdd7b79334630c
6
+ metadata.gz: 69bd74f1fdce25360acd89e299cbe245c85a2689d42852023a2bbd5da6bdad711feffe21364b0a53e0498263aa347ef34be7e106cd22152382b4762e57f7383a
7
+ data.tar.gz: 89c73e22308498ead5d605ad9dd6382b8ef0b463030f7a2a00a600c4debc1908f11d6f12830237b5507bc42b6d7560b2d22d0051a50c6a6055e1546c813ae419
data/README.md CHANGED
@@ -1,7 +1,18 @@
1
1
  # Frikandel
2
+ [![Gem Version](https://badge.fury.io/rb/frikandel.png)](http://badge.fury.io/rb/frikandel)
3
+ [![Build Status](https://api.travis-ci.org/taktsoft/frikandel.png)](https://travis-ci.org/taktsoft/frikandel)
2
4
 
3
- This Gem adds a TTL (Time To Live) Date to every cookie that your application sets. When the cookie has expired, the users session gets reset. This should help protect from Session-Fixation-Attacks.
5
+ This gem aims to improve the security of your rails application. It allows you to add a TTL (Time To Live) to the session cookie and allows you to bind the session to an IP address.
4
6
 
7
+ When the TTL expires or the IP address changes, the users session gets reset. This should help to make [session-fixation-attacks](http://guides.rubyonrails.org/security.html#session-fixation) harder to execute.
8
+
9
+
10
+ ## Security considerations
11
+
12
+ Consider the following attack vector: The web application under attack is a rails application. The application writes the user id in the session after a successful login. The attacker has obtained a valid session cookie from an authenticated user.
13
+ By default the cookie is valid indefinitely. If the application tries to "reset the session" it simply issues a new session cookie to the attackers browser. If the attacker just ignores the new session cookie and continues to use the old session cookie the application has no way of knowing that.
14
+
15
+ By adding a TTL the attack window gets smaller. An stolen has to be used within a given time slot. A reauthentication is enforced after a given time has passed. By adding IP address binding the attacker has to use the same ip address as the victim the session was stolen from.
5
16
 
6
17
  ## Requirements
7
18
 
@@ -28,11 +39,15 @@ Or install it yourself as:
28
39
 
29
40
  ## Usage
30
41
 
31
- To activate frikandel's Session-Fixation-Protection for your application, you only need to include a module in your `ApplicationController`:
42
+ You can use session TTL or the combination of TTL and IP address binding. Please be advised that the sole use of IP address binding doesn't protect from session-fixation-attacks.
43
+
44
+
45
+ To activate Frikandel's session-fixation-protection for your application, you only need to include the proper module(s) in your `ApplicationController`:
32
46
 
33
47
  ```ruby
34
48
  class ApplicationController < ActionController::Base
35
49
  include Frikandel::LimitSessionLifetime
50
+ include Frikandel::BindSessionToIpAddress
36
51
 
37
52
  # ...
38
53
  end
@@ -47,33 +62,37 @@ Frikandel::Configuration.max_ttl = 2.days
47
62
  Frikandel::Configuration.ttl = 4.hours
48
63
  ```
49
64
 
50
- The value at `Frikandel::Configuration.max_ttl` is the absolute value in seconds that a cookie is valid. In this example, all cookies will be invalidated after two days in all cases. This timestamp doesn't get refreshed.
65
+ The value at `Frikandel::Configuration.max_ttl` is the absolute value (in seconds) that a cookie is valid. In this example, all cookies will be invalidated after two days in all cases. This timestamp doesn't get refreshed. In a typical application that means the user has to re-login after this time. That's also the maximum time frame a stolen session can be used.
51
66
 
52
67
  The second value `Frikandel::Configuration.ttl` states how long (in seconds) a session/cookie is valid, when the cookie timestamp gets not refreshed. The timestamp gets refrehed everytime a user visits the site.
53
68
 
54
69
  The default values are `24.hours` for `max_ttl` and `2.hours` for `ttl`. If you are okay with this settings, you don't need to create an initializer for frikandel.
55
70
 
56
71
 
57
- ### Customize on_expired_session behavior
72
+ ### Customize on_invalid_session behavior
58
73
 
59
74
  You can also overwrite what should happen when a cookie times out on the controller-level. The default behaviour is to do a `reset_session` and `redirect_to root_path`. For example, if you want to overwrite the default behavior when a user is on the `PublicController`, you want to overwrite the `on_expired_session`-method in your controller:
60
75
 
61
76
  ```ruby
62
77
  class PublicController < ApplicationController
63
- def on_expired_session
78
+ def on_invalid_session
64
79
  raise "Your Session Has Expired! Oh No!"
65
80
  end
66
81
  end
67
82
  ```
68
83
 
69
- If you want to revert the original behavior in a sub-class of your `PublicController`, you simply re-alias the method to `original_on_expired_session` like this:
84
+ If you want to revert the original behavior in a sub-class of your `PublicController`, you simply re-alias the method to `original_on_invalid_session` like this:
70
85
 
71
86
  ```ruby
72
87
  class AdminController < PublicController
73
- alias on_expired_session original_on_expired_session
88
+ alias on_invalid_session original_on_invalid_session
74
89
  end
75
90
  ```
76
91
 
92
+ ## Changes
93
+
94
+ 2.0.0 Added IP address binding. Renamed callback from 'on_expired_session' to 'on_invalid_session'.
95
+
77
96
  ## Contributing
78
97
  1. Fork it
79
98
  2. Create your feature branch (git checkout -b my-new-feature)
@@ -1,32 +1,5 @@
1
1
  require "frikandel/version"
2
2
  require "frikandel/configuration"
3
-
4
- module Frikandel
5
- module LimitSessionLifetime
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- append_before_filter :validate_session_timestamp
10
- append_after_filter :persist_session_timestamp
11
- end
12
-
13
- private
14
-
15
- def validate_session_timestamp
16
- if session.key?(:ttl) && session.key?(:max_ttl) && (session[:ttl] < Frikandel::Configuration.ttl.ago || session[:max_ttl] < Time.now)
17
- on_expired_session
18
- end
19
- end
20
-
21
- def persist_session_timestamp
22
- session[:ttl] = Time.now
23
- session[:max_ttl] ||= Frikandel::Configuration.max_ttl.from_now
24
- end
25
-
26
- def on_expired_session
27
- reset_session
28
- redirect_to root_path
29
- end
30
- alias original_on_expired_session on_expired_session
31
- end
32
- end
3
+ require 'frikandel/session_invalidation'
4
+ require 'frikandel/limit_session_lifetime'
5
+ require 'frikandel/bind_session_to_ip_address'
@@ -0,0 +1,33 @@
1
+ module Frikandel
2
+ module BindSessionToIpAddress
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ append_before_filter :validate_session_ip_address
7
+ append_after_filter :persist_session_ip_address
8
+ end
9
+
10
+ private
11
+
12
+ def validate_session_ip_address
13
+ if session.key?(:ip_address) && !ip_address_match_with_current?(session[:ip_address])
14
+ on_invalid_session
15
+ elsif !session.key?(:ip_address)
16
+ reset_session
17
+ end
18
+ end
19
+
20
+ def persist_session_ip_address
21
+ session[:ip_address] = current_ip_address
22
+ end
23
+
24
+ def current_ip_address
25
+ request.remote_ip
26
+ end
27
+
28
+ def ip_address_match_with_current?(ip_address)
29
+ current_ip_address == ip_address
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ module Frikandel
2
+ module LimitSessionLifetime
3
+ extend ActiveSupport::Concern
4
+ include SessionInvalidation
5
+
6
+ included do
7
+ append_before_filter :validate_session_timestamp
8
+ append_after_filter :persist_session_timestamp
9
+ end
10
+
11
+ private
12
+
13
+ def validate_session_timestamp
14
+ if session.key?(:ttl) && session.key?(:max_ttl) && (session[:ttl] < Frikandel::Configuration.ttl.ago || session[:max_ttl] < Time.now)
15
+ on_invalid_session
16
+ elsif !session.key?(:ttl) && !session.key?(:max_ttl)
17
+ reset_session
18
+ end
19
+ end
20
+
21
+ def persist_session_timestamp
22
+ session[:ttl] = Time.now
23
+ session[:max_ttl] ||= Frikandel::Configuration.max_ttl.from_now
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module Frikandel
2
+ module SessionInvalidation
3
+
4
+ private
5
+
6
+ def on_invalid_session
7
+ reset_session
8
+ redirect_to root_path
9
+ end
10
+ alias original_on_invalid_session on_invalid_session
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Frikandel
2
- VERSION = "1.0.0"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+ require "support/application_controller"
3
+
4
+ class BindSessionToIpAddressController < ApplicationController
5
+ include Frikandel::BindSessionToIpAddress
6
+
7
+ def home
8
+ render text: "bind test"
9
+ end
10
+ end
11
+
12
+ describe BindSessionToIpAddressController do
13
+ it "writes current ip address to session" do
14
+ expect(session[:ip_address]).to be_nil
15
+ get :home
16
+ expect(session[:ip_address]).to eql("0.0.0.0")
17
+ end
18
+
19
+ it "raises an exception if session address and current ip address don't match" do
20
+ session[:ip_address] = "1.2.3.4"
21
+ controller.should_receive(:on_invalid_session)
22
+
23
+ get :home
24
+ end
25
+
26
+
27
+ context "ip address isn't present in session" do
28
+ it "resets the session" do
29
+ session[:user_id] = 4337
30
+ get :home
31
+
32
+ session[:user_id].should be_blank
33
+ end
34
+
35
+ it "allows the request to be rendered as normal" do
36
+ get :home
37
+
38
+ response.body.should eql("bind test")
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+ require "support/application_controller"
3
+
4
+ class CombinedController < ApplicationController
5
+ include Frikandel::LimitSessionLifetime
6
+ include Frikandel::BindSessionToIpAddress
7
+
8
+ def home
9
+ render text: "combined test"
10
+ end
11
+ end
12
+
13
+ describe CombinedController do
14
+ context "ttl nor ip isn't present in session" do
15
+ it "resets the session" do
16
+ session[:user_id] = 4337
17
+ get :home
18
+
19
+ session[:user_id].should be_blank
20
+ session[:ttl].should be_present
21
+ session[:ip_address].should be_present
22
+ end
23
+
24
+ it "allows the request to be rendered as normal" do
25
+ get :home
26
+
27
+ response.body.should eql("combined test")
28
+ end
29
+ end
30
+
31
+ context "ttl or ip isn't present in session" do
32
+ it "resets the session if ip address is missing" do
33
+ session[:user_id] = 4337
34
+ session[:ttl] = "Something"
35
+ get :home
36
+
37
+ session[:user_id].should be_blank
38
+
39
+ session[:ttl].should be_present
40
+ session[:ttl].should_not eql("Something")
41
+ session[:ip_address].should be_present
42
+ end
43
+
44
+ it "resets the session if ttl is missing" do
45
+ session[:user_id] = 4337
46
+ session[:ip_address] = "Something"
47
+ get :home
48
+
49
+ session[:user_id].should be_blank
50
+
51
+ session[:ttl].should be_present
52
+ session[:ip_address].should be_present
53
+ session[:ip_address].should eql("0.0.0.0")
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+ require "support/application_controller"
3
+
4
+ class SessionInvalidError < StandardError; end
5
+
6
+ class CustomizedOnInvalidSessionController < ApplicationController
7
+ include Frikandel::LimitSessionLifetime
8
+
9
+ def on_invalid_session
10
+ raise SessionInvalidError.new("Your Session is DEAD!")
11
+ end
12
+ alias my_on_invalid_session on_invalid_session
13
+ end
14
+
15
+ describe CustomizedOnInvalidSessionController do
16
+
17
+ it "uses the overwritten on_invalid_cookie function" do
18
+ get :home
19
+ request.session[:max_ttl] = 1.minute.ago
20
+
21
+ expect { get :home }.to raise_error SessionInvalidError
22
+ end
23
+
24
+ it "can revert the on_invalid_cookie function back to the original" do
25
+ # NOTE: Don't confuse original_on_invalid_session with my_on_invalid_session!
26
+ class CustomizedOnInvalidSessionController < ApplicationController
27
+ alias on_invalid_session original_on_invalid_session # Setting it to the Gems original
28
+ end
29
+
30
+ get :home
31
+ request.session[:max_ttl] = 1.minute.ago
32
+
33
+ begin
34
+ expect { get :home }.to_not raise_error
35
+ ensure
36
+ class CustomizedOnInvalidSessionController < ApplicationController
37
+ alias on_invalid_session my_on_invalid_session # Reverting it back to the Customized function thats defined in this test
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,8 +1,15 @@
1
1
  require "spec_helper"
2
2
  require "support/application_controller"
3
3
 
4
- describe ApplicationController do
4
+ class LimitSessionLifetimeController < ApplicationController
5
+ include Frikandel::LimitSessionLifetime
5
6
 
7
+ def home
8
+ render text: "ttl test"
9
+ end
10
+ end
11
+
12
+ describe LimitSessionLifetimeController do
6
13
  it "holds the session for at least .1 seconds" do
7
14
  get :home
8
15
  session[:user_id] = 1337
@@ -31,16 +38,6 @@ describe ApplicationController do
31
38
 
32
39
  session[:user_id].should be_blank
33
40
  end
34
-
35
- it "works when there was no session in the request" do
36
- get :home
37
- session[:user_id] = 4337
38
- request.session = nil
39
- get :home
40
-
41
- session[:user_id].should be_blank
42
- end
43
-
44
41
  it "is configurable" do
45
42
  old_value = Frikandel::Configuration.ttl
46
43
  Frikandel::Configuration.ttl = 1.minute
@@ -54,4 +51,18 @@ describe ApplicationController do
54
51
  Frikandel::Configuration.ttl = old_value
55
52
  end
56
53
 
54
+ context "ttl isn't present in session" do
55
+ it "resets the session" do
56
+ session[:user_id] = 4337
57
+ get :home
58
+
59
+ session[:user_id].should be_blank
60
+ end
61
+
62
+ it "allows the request to be rendered as normal" do
63
+ get :home
64
+
65
+ response.body.should eql("ttl test")
66
+ end
67
+ end
57
68
  end
@@ -1,13 +1,13 @@
1
1
 
2
2
  Rails.application.routes.draw do
3
- get "/home" => "application#home", as: :root
4
- get "/customized_controller_home" => "customized_on_expired_session#home", as: :customized_controller_home
3
+ root to: "application#home"
4
+ get "/limit_session_lifetime_home" => "limit_session_lifetime#home"
5
+ get "/customized_controller_home" => "customized_on_invalid_session#home", as: :customized_controller_home
6
+ get "/bind_session_to_ip_address_home" => "bind_session_to_ip_address#home"
7
+ get "/combined_controller_home" => "combined#home"
5
8
  end
6
9
 
7
-
8
10
  class ApplicationController < ActionController::Base
9
- include Frikandel::LimitSessionLifetime
10
-
11
11
  protect_from_forgery with: :exception
12
12
 
13
13
  def home
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: frikandel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taktsoft
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-07 00:00:00.000000000 Z
11
+ date: 2014-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -131,10 +131,15 @@ files:
131
131
  - Rakefile
132
132
  - frikandel.gemspec
133
133
  - lib/frikandel.rb
134
+ - lib/frikandel/bind_session_to_ip_address.rb
134
135
  - lib/frikandel/configuration.rb
136
+ - lib/frikandel/limit_session_lifetime.rb
137
+ - lib/frikandel/session_invalidation.rb
135
138
  - lib/frikandel/version.rb
136
- - spec/controllers/application_controller_spec.rb
137
- - spec/controllers/customized_on_expired_cookie_controller_spec.rb
139
+ - spec/controllers/bind_session_to_ip_address_controller_spec.rb
140
+ - spec/controllers/combined_controller_spec.rb
141
+ - spec/controllers/customized_on_invalid_session_controller_spec.rb
142
+ - spec/controllers/limit_session_lifetime_controller_spec.rb
138
143
  - spec/dummy/README.rdoc
139
144
  - spec/dummy/Rakefile
140
145
  - spec/dummy/app/assets/images/.keep
@@ -197,46 +202,48 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
202
  version: '0'
198
203
  requirements: []
199
204
  rubyforge_project:
200
- rubygems_version: 2.2.1
205
+ rubygems_version: 2.2.2
201
206
  signing_key:
202
207
  specification_version: 4
203
208
  summary: This gem adds a ttl to the session cookie of your application.
204
209
  test_files:
205
- - spec/dummy/app/controllers/application_controller.rb
206
- - spec/dummy/app/helpers/application_helper.rb
207
- - spec/dummy/app/views/layouts/application.html.erb
208
- - spec/dummy/app/assets/stylesheets/application.css
209
- - spec/dummy/app/assets/javascripts/application.js
210
- - spec/dummy/public/404.html
210
+ - spec/lib/frikandel/configuration_spec.rb
211
+ - spec/spec_helper.rb
212
+ - spec/controllers/bind_session_to_ip_address_controller_spec.rb
213
+ - spec/controllers/combined_controller_spec.rb
214
+ - spec/controllers/limit_session_lifetime_controller_spec.rb
215
+ - spec/controllers/customized_on_invalid_session_controller_spec.rb
216
+ - spec/support/application_controller.rb
217
+ - spec/dummy/README.rdoc
218
+ - spec/dummy/db/test.sqlite3
211
219
  - spec/dummy/public/500.html
212
- - spec/dummy/public/422.html
213
220
  - spec/dummy/public/favicon.ico
221
+ - spec/dummy/public/422.html
222
+ - spec/dummy/public/404.html
214
223
  - spec/dummy/config.ru
215
- - spec/dummy/README.rdoc
216
- - spec/dummy/db/test.sqlite3
217
224
  - spec/dummy/log/test.log
218
- - spec/dummy/config/routes.rb
219
- - spec/dummy/config/application.rb
220
- - spec/dummy/config/database.yml
221
- - spec/dummy/config/environments/development.rb
222
- - spec/dummy/config/environments/test.rb
223
- - spec/dummy/config/environments/production.rb
224
- - spec/dummy/config/environment.rb
225
- - spec/dummy/config/locales/en.yml
226
- - spec/dummy/config/initializers/backtrace_silencers.rb
225
+ - spec/dummy/config/boot.rb
227
226
  - spec/dummy/config/initializers/wrap_parameters.rb
228
227
  - spec/dummy/config/initializers/filter_parameter_logging.rb
228
+ - spec/dummy/config/initializers/session_store.rb
229
+ - spec/dummy/config/initializers/secret_token.rb
229
230
  - spec/dummy/config/initializers/mime_types.rb
231
+ - spec/dummy/config/initializers/backtrace_silencers.rb
230
232
  - spec/dummy/config/initializers/inflections.rb
231
- - spec/dummy/config/initializers/secret_token.rb
232
- - spec/dummy/config/initializers/session_store.rb
233
- - spec/dummy/config/boot.rb
234
- - spec/dummy/Rakefile
235
- - spec/dummy/bin/rake
233
+ - spec/dummy/config/locales/en.yml
234
+ - spec/dummy/config/routes.rb
235
+ - spec/dummy/config/environment.rb
236
+ - spec/dummy/config/environments/production.rb
237
+ - spec/dummy/config/environments/test.rb
238
+ - spec/dummy/config/environments/development.rb
239
+ - spec/dummy/config/database.yml
240
+ - spec/dummy/config/application.rb
236
241
  - spec/dummy/bin/rails
237
242
  - spec/dummy/bin/bundle
238
- - spec/lib/frikandel/configuration_spec.rb
239
- - spec/controllers/application_controller_spec.rb
240
- - spec/controllers/customized_on_expired_cookie_controller_spec.rb
241
- - spec/spec_helper.rb
242
- - spec/support/application_controller.rb
243
+ - spec/dummy/bin/rake
244
+ - spec/dummy/app/assets/javascripts/application.js
245
+ - spec/dummy/app/assets/stylesheets/application.css
246
+ - spec/dummy/app/views/layouts/application.html.erb
247
+ - spec/dummy/app/controllers/application_controller.rb
248
+ - spec/dummy/app/helpers/application_helper.rb
249
+ - spec/dummy/Rakefile
@@ -1,39 +0,0 @@
1
- require "spec_helper"
2
- require "support/application_controller"
3
-
4
- class SessionExpiredError < StandardError; end
5
-
6
- class CustomizedOnExpiredSessionController < ApplicationController
7
- def on_expired_session
8
- raise SessionExpiredError.new("Your Session is DEAD!")
9
- end
10
- alias my_on_expired_session on_expired_session
11
- end
12
-
13
- describe CustomizedOnExpiredSessionController do
14
-
15
- it "uses the overwritten on_expired_cookie function" do
16
- get :home
17
- request.session[:max_ttl] = 1.minute.ago
18
-
19
- expect { get :home }.to raise_error SessionExpiredError
20
- end
21
-
22
- it "can revert the on_expired_cookie function back to the original" do
23
- # NOTE: Don't confuse original_on_expired_session with my_on_expired_session!
24
- class CustomizedOnExpiredSessionController < ApplicationController
25
- alias on_expired_session original_on_expired_session # Setting it to the Gems original
26
- end
27
-
28
- get :home
29
- request.session[:max_ttl] = 1.minute.ago
30
-
31
- begin
32
- expect { get :home }.to_not raise_error
33
- ensure
34
- class CustomizedOnExpiredSessionController < ApplicationController
35
- alias on_expired_session my_on_expired_session # Reverting it back to the Customized function thats defined in this test
36
- end
37
- end
38
- end
39
- end