rack-ssl-enforcer 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Rack::SslEnforcer [![Build Status](https://secure.travis-ci.org/tobmatth/rack-ssl-enforcer.png?branch=master)](http://travis-ci.org/tobmatth/rack-ssl-enforcer)
1
+ # Rack::SslEnforcer [![Build Status](https://travis-ci.org/tobmatth/rack-ssl-enforcer.png?branch=master)](https://travis-ci.org/tobmatth/rack-ssl-enforcer)
2
2
 
3
3
  Rack::SslEnforcer is a simple Rack middleware to enforce SSL connections. As of Version 0.2.0, Rack::SslEnforcer marks
4
4
  Cookies as secure by default (HSTS must be set manually).
@@ -7,7 +7,7 @@ Tested against Ruby 1.8.7, 1.9.2, 1.9.3, ruby-head, REE and the latest versions
7
7
 
8
8
  ## Installation
9
9
 
10
- The simplest way to install Rack::SslEnforcer is to use [Bundler](http://gembundler.com/).
10
+ The simplest way to install Rack::SslEnforcer is to use [Bundler](http://gembundler.com).
11
11
 
12
12
  Add Rack::SslEnforcer to your `Gemfile`:
13
13
 
@@ -20,23 +20,24 @@ Add Rack::SslEnforcer to your `Gemfile`:
20
20
  In order for Rack::SslEnforcer to properly work it has to be at the top
21
21
  of the Rack Middleware.
22
22
 
23
- Using `enable :session` will place Rack::Session::Cookie before Rack::Ssl::Enforcer
23
+ Using `enable :session` will place Rack::Session::Cookie before Rack::Ssl::Enforcer
24
24
  and will prevent Rack::Ssl::Enforcer from marking cookies as secure.
25
25
 
26
26
  To fix this issue do not use `enable :sessions` instead add the
27
- Rack::Session::Cookie middleware after Rack::Ssl::Enforcer.
27
+ Rack::Session::Cookie middleware after Rack::Ssl::Enforcer.
28
28
 
29
29
  Eg:
30
30
 
31
- use Rack::SslEnforcer
32
- set :session_secret, 'asdfa2342923422f1adc05c837fa234230e3594b93824b00e930ab0fb94b'
33
-
34
- #Enable sinatra sessions
35
- use Rack::Session::Cookie, :key => '_rack_session',
36
- :path => '/',
37
- :expire_after => 2592000, # In seconds
38
- :secret => session_secret
39
-
31
+ ```ruby
32
+ use Rack::SslEnforcer
33
+ set :session_secret, 'asdfa2342923422f1adc05c837fa234230e3594b93824b00e930ab0fb94b'
34
+
35
+ #Enable sinatra sessions
36
+ use Rack::Session::Cookie, :key => '_rack_session',
37
+ :path => '/',
38
+ :expire_after => 2592000, # In seconds
39
+ :secret => settings.session_secret
40
+ ```
40
41
 
41
42
  ## Basic Usage
42
43
 
@@ -72,7 +73,7 @@ config.middleware.use Rack::SslEnforcer, :only_hosts => [/[secure|admin]\.exampl
72
73
 
73
74
  ### Path constraints
74
75
 
75
- You can enforce SSL connections only for certain paths with `:only`, or prevent certain paths from being forced to SSL with `:except`. Constraints can be a `String`, a `Regex` or an array of `String` or `Regex` (possibly mixed), as shown in the following examples:
76
+ You can enforce SSL connections only for certain paths with `:only`, prevent certain paths from being forced to SSL with `:except`, or - if you don't care how certain paths are accessed - ignore them with `:ignore`. Constraints can be a `String`, a `Regex` or an array of `String` or `Regex` (possibly mixed), as shown in the following examples:
76
77
 
77
78
  ```ruby
78
79
  config.middleware.use Rack::SslEnforcer, :only => '/login'
@@ -81,6 +82,11 @@ config.middleware.use Rack::SslEnforcer, :only => '/login'
81
82
  config.middleware.use Rack::SslEnforcer, :only => %r{^/admin/}
82
83
 
83
84
  config.middleware.use Rack::SslEnforcer, :except => ['/demo', %r{^/public/}]
85
+
86
+ config.middleware.use Rack::SslEnforcer, :ignore => '/assets'
87
+
88
+ # You can also combine multiple constraints
89
+ config.middleware.use Rack::SslEnforcer, :only => '/cart', :ignore => %r{/assets}, :strict => true
84
90
  ```
85
91
 
86
92
  ### Method constraints
@@ -97,6 +103,35 @@ config.middleware.use Rack::SslEnforcer, :except_methods => ['GET', 'HEAD']
97
103
 
98
104
  Note: The `:hosts` constraint takes precedence over the `:path` constraint. Please see the tests for examples.
99
105
 
106
+
107
+ ### Environment constraints
108
+
109
+ You can enforce SSL connections only for certain environments with `:only_environments` or prevent certain environments from being forced to SSL with `:except_environments`.
110
+ Environment constraints may be a `String`, a `Regex` or an array of `String` or `Regex` (possibly mixed), as shown in the following examples:
111
+
112
+ ```ruby
113
+ config.middleware.use Rack::SslEnforcer, :except_environments => 'development'
114
+
115
+ config.middleware.use Rack::SslEnforcer, :except_environments => /^[0-9a-f]+_local$/i
116
+
117
+ config.middleware.use Rack::SslEnforcer, :only_environments => ['production', /^QA/]
118
+ ```
119
+
120
+ Note: The `:environments` constraint requires one the following environment variables to be set: `RACK_ENV`, `RAILS_ENV`, `ENV`.
121
+
122
+ ### User agent constraints
123
+
124
+ You can enforce SSL connections only for certain user agents with `:only_agents` or prevent certain user agents from being forced to SSL with `:except_agents`.
125
+ User agent constraints may be a `String`, a `Regex` or an array of `String` or `Regex` (possibly mixed), as shown in the following examples:
126
+
127
+ ```ruby
128
+ config.middleware.use Rack::SslEnforcer, :except_agents => 'Googlebot'
129
+
130
+ config.middleware.use Rack::SslEnforcer, :except_agents => /[Googlebot|bingbot]/
131
+
132
+ config.middleware.use Rack::SslEnforcer, :only_agents => ['test-secu-bot', /custom-crawler[0-9a-f]/]
133
+ ```
134
+
100
135
  ### Force-redirection to non-SSL connection if constraint is not matched
101
136
 
102
137
  Use the `:strict` option to force non-SSL connection for all requests not matching the constraints you set. Examples:
@@ -137,6 +172,14 @@ You might need the `:redirect_to` option if the requested URL can't be determine
137
172
  config.middleware.use Rack::SslEnforcer, :redirect_to => 'https://example.org'
138
173
  ```
139
174
 
175
+ ### Redirect with specific HTTP status code
176
+
177
+ By default it redirects with HTTP status code 301(Moved Permanently). Sometimes you might need to redirect with different HTTP status code:
178
+
179
+ ```ruby
180
+ config.middleware.use Rack::SslEnforcer, :redirect_code => 302
181
+ ```
182
+
140
183
  ### Custom HTTP port
141
184
 
142
185
  If you're using a different port than the default (80) for HTTP, you can specify it with the `:http_port` option:
@@ -167,6 +210,19 @@ config.middleware.use Rack::SslEnforcer, :only => "/login", :force_secure_cookie
167
210
  But be aware that if you do so, you have to make sure that the content of you cookie is encoded.
168
211
  This can be done using a coder with [Rack::Session::Cookie](https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb#L28-42).
169
212
 
213
+ ### Running code before redirect
214
+
215
+ You may want to run some code before rack-ssl-enforcer forces a redirect. This code could do something like log that a redirect was done, or if this is used in a Rails app, keeping the flash when redirecting:
216
+
217
+
218
+ ```ruby
219
+ config.middleware.use Rack::SslEnforcer, :only => '/login', :before_redirect => Proc.new {
220
+ #keep flash on redirect
221
+ @request.session[:flash].keep if !@request.session.nil? && @request.session.key?('flash') && !@request.session['flash'].empty?
222
+ }
223
+ ```
224
+
225
+
170
226
  ## Deployment
171
227
 
172
228
  If you run your application behind a proxy (e.g. Nginx) you may need to do some configuration on that side. If you don't you may experience an infinite redirect loop.
@@ -195,13 +251,9 @@ This makes sure that Rack::SslEnforcer knows it's being accessed over SSL. Just
195
251
 
196
252
  * Cleanup tests
197
253
 
198
- ## Contributors
254
+ #### Contributors
199
255
 
200
- * [Dan Mayer](http://github.com/danmayer)
201
- * [Rémy Coutable](http://github.com/rymai)
202
- * [Thibaud Guillaume-Gentil](http://github.com/thibaudgg)
203
- * [Paul Annesley](https://github.com/pda)
204
- * [Saimon Moore](https://github.com/saimonmoore)
256
+ [https://github.com/tobmatth/rack-ssl-enforcer/graphs/contributors](https://github.com/tobmatth/rack-ssl-enforcer/graphs/contributors)
205
257
 
206
258
  ## Credits
207
259
 
@@ -217,4 +269,4 @@ Flagging cookies as secure functionality and HSTS support is greatly inspired by
217
269
 
218
270
  ## Copyright
219
271
 
220
- Copyright (c) 2010-2012 Tobias Matthies. See LICENSE for details.
272
+ Copyright (c) 2010-2013 Tobias Matthies. See [LICENSE](https://github.com/tobmatth/rack-ssl-enforcer/blob/master/LICENSE) for details.
@@ -5,9 +5,11 @@ module Rack
5
5
  class SslEnforcer
6
6
 
7
7
  CONSTRAINTS_BY_TYPE = {
8
- :hosts => [:only_hosts, :except_hosts],
9
- :path => [:only, :except],
10
- :methods => [:only_methods, :except_methods]
8
+ :hosts => [:only_hosts, :except_hosts],
9
+ :agents => [:only_agents, :except_agents],
10
+ :path => [:only, :except],
11
+ :methods => [:only_methods, :except_methods],
12
+ :environments => [:only_environments, :except_environments]
11
13
  }
12
14
 
13
15
  # Warning: If you set the option force_secure_cookies to false, make sure that your cookies
@@ -15,14 +17,18 @@ module Rack
15
17
  def initialize(app, options={})
16
18
  default_options = {
17
19
  :redirect_to => nil,
20
+ :redirect_code => nil,
18
21
  :strict => false,
19
22
  :mixed => false,
20
23
  :hsts => nil,
21
24
  :http_port => nil,
22
25
  :https_port => nil,
23
- :force_secure_cookies => true
26
+ :force_secure_cookies => true,
27
+ :before_redirect => nil
24
28
  }
25
- CONSTRAINTS_BY_TYPE.values.each { |constraint| default_options[constraint] = nil }
29
+ CONSTRAINTS_BY_TYPE.values.each do |constraints|
30
+ constraints.each { |constraint| default_options[constraint] = nil }
31
+ end
26
32
 
27
33
  @app, @options = app, default_options.merge(options)
28
34
  end
@@ -39,7 +45,8 @@ module Rack
39
45
  end
40
46
 
41
47
  if redirect_required?
42
- modify_location_and_redirect
48
+ call_before_redirect
49
+ modify_location_and_redirect
43
50
  elsif ssl_request?
44
51
  status, headers, body = @app.call(env)
45
52
  flag_cookies_as_secure!(headers) if @options[:force_secure_cookies]
@@ -51,7 +58,7 @@ module Rack
51
58
  end
52
59
 
53
60
  private
54
-
61
+
55
62
  def redirect_required?
56
63
  scheme_mismatch? || host_mismatch?
57
64
  end
@@ -66,31 +73,35 @@ module Rack
66
73
  false
67
74
  end
68
75
  end
69
-
76
+
70
77
  def scheme_mismatch?
71
78
  @scheme && @scheme != current_scheme
72
79
  end
73
-
80
+
74
81
  def host_mismatch?
75
82
  destination_host && destination_host != @request.host
76
83
  end
77
-
84
+
85
+ def call_before_redirect
86
+ @options[:before_redirect].call unless @options[:before_redirect].nil?
87
+ end
88
+
78
89
  def modify_location_and_redirect
79
90
  location = "#{current_scheme}://#{@request.host}#{@request.fullpath}"
80
91
  location = replace_scheme(location, @scheme)
81
92
  location = replace_host(location, @options[:redirect_to])
82
93
  redirect_to(location)
83
94
  end
84
-
95
+
85
96
  def redirect_to(location)
86
97
  body = "<html><body>You are being <a href=\"#{location}\">redirected</a>.</body></html>"
87
- [301, { 'Content-Type' => 'text/html', 'Location' => location }, [body]]
98
+ [@options[:redirect_code] || 301, { 'Content-Type' => 'text/html', 'Location' => location }, [body]]
88
99
  end
89
100
 
90
101
  def ssl_request?
91
102
  current_scheme == 'https'
92
103
  end
93
-
104
+
94
105
  def destination_host
95
106
  if @options[:redirect_to]
96
107
  host_parts = URI.split(@options[:redirect_to])
@@ -100,7 +111,7 @@ module Rack
100
111
 
101
112
  # Fixed in rack >= 1.3
102
113
  def current_scheme
103
- if @request.env['HTTPS'] == 'on'
114
+ if @request.env['HTTPS'] == 'on' || @request.env['HTTP_X_SSL_REQUEST'] == 'on'
104
115
  'https'
105
116
  elsif @request.env['HTTP_X_FORWARDED_PROTO']
106
117
  @request.env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
@@ -116,7 +127,7 @@ module Rack
116
127
  else
117
128
  provided_keys.all? do |key|
118
129
  rules = [@options[key]].flatten.compact
119
- rules.send([:except_hosts, :except].include?(key) ? :all? : :any?) do |rule|
130
+ rules.send([:except_hosts, :except_agents, :except_environments, :except].include?(key) ? :all? : :any?) do |rule|
120
131
  SslEnforcerConstraint.new(key, rule, @request).matches?
121
132
  end
122
133
  end
@@ -135,24 +146,24 @@ module Rack
135
146
 
136
147
  def replace_scheme(uri, scheme)
137
148
  return uri if not scheme_mismatch?
138
-
149
+
139
150
  port = adjust_port_to(scheme)
140
151
  uri_parts = URI.split(uri)
141
152
  uri_parts[3] = port unless port.nil?
142
153
  uri_parts[0] = scheme
143
154
  URI::HTTP.new(*uri_parts).to_s
144
155
  end
145
-
156
+
146
157
  def replace_host(uri, host)
147
158
  return uri unless host_mismatch?
148
-
159
+
149
160
  host_parts = URI.split(host)
150
161
  new_host = host_parts[2] || host_parts[5]
151
162
  uri_parts = URI.split(uri)
152
163
  uri_parts[2] = new_host
153
164
  URI::HTTPS.new(*uri_parts).to_s
154
165
  end
155
-
166
+
156
167
  def adjust_port_to(scheme)
157
168
  if scheme == 'https'
158
169
  @options[:https_port] if @options[:https_port] && @options[:https_port] != URI::HTTPS.default_port
@@ -170,7 +181,7 @@ module Rack
170
181
  end
171
182
 
172
183
  headers['Set-Cookie'] = cookies.map do |cookie|
173
- cookie !~ / secure;/ ? "#{cookie}; secure" : cookie
184
+ cookie !~ /(^|;\s)secure($|;)/ ? "#{cookie}; secure" : cookie
174
185
  end.join("\n")
175
186
  end
176
187
  end
@@ -11,12 +11,12 @@ class SslEnforcerConstraint
11
11
  else
12
12
  result = tested_string.send(operator, @rule)
13
13
  end
14
-
14
+
15
15
  negate_result? ? !result : result
16
16
  end
17
17
 
18
18
  private
19
-
19
+
20
20
  def negate_result?
21
21
  @name.to_s =~ /except/
22
22
  end
@@ -31,6 +31,10 @@ private
31
31
  @request.host
32
32
  when /methods/
33
33
  @request.request_method
34
+ when /environments/
35
+ ENV["RACK_ENV"] || ENV["RAILS_ENV"] || ENV["ENV"]
36
+ when /agents/
37
+ @request.user_agent
34
38
  else
35
39
  @request.path
36
40
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class SslEnforcer
3
- VERSION = "0.2.5"
3
+ VERSION = "0.2.6"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,112 +1,105 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rack-ssl-enforcer
3
- version: !ruby/object:Gem::Version
4
- hash: 29
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.6
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 5
10
- version: 0.2.5
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Tobias Matthies
14
9
  - Thibaud Guillaume-Gentil
15
10
  autorequire:
16
11
  bindir: bin
17
12
  cert_chain: []
18
-
19
- date: 2012-11-14 00:00:00 Z
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
13
+ date: 2013-09-18 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: bundler
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
19
+ requirements:
27
20
  - - ~>
28
- - !ruby/object:Gem::Version
29
- hash: 15
30
- segments:
31
- - 1
32
- - 0
33
- version: "1.0"
21
+ - !ruby/object:Gem::Version
22
+ version: '1.0'
34
23
  type: :development
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: test-unit
38
24
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '1.0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: test-unit
33
+ requirement: !ruby/object:Gem::Requirement
40
34
  none: false
41
- requirements:
35
+ requirements:
42
36
  - - ~>
43
- - !ruby/object:Gem::Version
44
- hash: 5
45
- segments:
46
- - 2
47
- - 3
48
- version: "2.3"
37
+ - !ruby/object:Gem::Version
38
+ version: '2.3'
49
39
  type: :development
50
- version_requirements: *id002
51
- - !ruby/object:Gem::Dependency
52
- name: shoulda
53
40
  prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
41
+ version_requirements: !ruby/object:Gem::Requirement
55
42
  none: false
56
- requirements:
43
+ requirements:
57
44
  - - ~>
58
- - !ruby/object:Gem::Version
59
- hash: 37
60
- segments:
61
- - 2
62
- - 11
63
- - 3
45
+ - !ruby/object:Gem::Version
46
+ version: '2.3'
47
+ - !ruby/object:Gem::Dependency
48
+ name: shoulda
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
64
54
  version: 2.11.3
65
55
  type: :development
66
- version_requirements: *id003
67
- - !ruby/object:Gem::Dependency
68
- name: rack
69
56
  prerelease: false
70
- requirement: &id004 !ruby/object:Gem::Requirement
57
+ version_requirements: !ruby/object:Gem::Requirement
71
58
  none: false
72
- requirements:
59
+ requirements:
73
60
  - - ~>
74
- - !ruby/object:Gem::Version
75
- hash: 31
76
- segments:
77
- - 1
78
- - 2
79
- - 0
61
+ - !ruby/object:Gem::Version
62
+ version: 2.11.3
63
+ - !ruby/object:Gem::Dependency
64
+ name: rack
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
80
70
  version: 1.2.0
81
71
  type: :development
82
- version_requirements: *id004
83
- - !ruby/object:Gem::Dependency
84
- name: rack-test
85
72
  prerelease: false
86
- requirement: &id005 !ruby/object:Gem::Requirement
73
+ version_requirements: !ruby/object:Gem::Requirement
87
74
  none: false
88
- requirements:
75
+ requirements:
89
76
  - - ~>
90
- - !ruby/object:Gem::Version
91
- hash: 3
92
- segments:
93
- - 0
94
- - 5
95
- - 4
77
+ - !ruby/object:Gem::Version
78
+ version: 1.2.0
79
+ - !ruby/object:Gem::Dependency
80
+ name: rack-test
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
96
86
  version: 0.5.4
97
87
  type: :development
98
- version_requirements: *id005
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ~>
93
+ - !ruby/object:Gem::Version
94
+ version: 0.5.4
99
95
  description: Rack::SslEnforcer is a simple Rack middleware to enforce ssl connections
100
- email:
101
- - tm@mit2m.de
96
+ email:
97
+ - tm@mit2m.d
102
98
  - thibaud@thibaud.me
103
99
  executables: []
104
-
105
100
  extensions: []
106
-
107
101
  extra_rdoc_files: []
108
-
109
- files:
102
+ files:
110
103
  - lib/rack/ssl-enforcer/constraint.rb
111
104
  - lib/rack/ssl-enforcer/version.rb
112
105
  - lib/rack/ssl-enforcer.rb
@@ -115,38 +108,29 @@ files:
115
108
  - README.md
116
109
  homepage: http://github.com/tobmatth/rack-ssl-enforcer
117
110
  licenses: []
118
-
119
111
  post_install_message:
120
112
  rdoc_options: []
121
-
122
- require_paths:
113
+ require_paths:
123
114
  - lib
124
- required_ruby_version: !ruby/object:Gem::Requirement
115
+ required_ruby_version: !ruby/object:Gem::Requirement
125
116
  none: false
126
- requirements:
127
- - - ">="
128
- - !ruby/object:Gem::Version
129
- hash: 3
130
- segments:
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ segments:
131
122
  - 0
132
- version: "0"
133
- required_rubygems_version: !ruby/object:Gem::Requirement
123
+ hash: 1004214355
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
125
  none: false
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- hash: 23
139
- segments:
140
- - 1
141
- - 3
142
- - 6
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
143
129
  version: 1.3.6
144
130
  requirements: []
145
-
146
131
  rubyforge_project: rack-ssl-enforcer
147
- rubygems_version: 1.8.24
132
+ rubygems_version: 1.8.25
148
133
  signing_key:
149
134
  specification_version: 3
150
135
  summary: A simple Rack middleware to enforce SSL
151
136
  test_files: []
152
-