rack-ssl-enforcer 0.2.0 → 0.2.1

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.
@@ -1,38 +1,64 @@
1
1
  = Rack::SslEnforcer
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
- Cookies as secure and enables HSTS by default.
5
-
4
+ Cookies as secure by default (HSTS must be set manually).
5
+ Tested on Ruby 1.8.7 & 1.9.2
6
6
 
7
7
  == Installation
8
8
 
9
9
  gem install rack-ssl-enforcer
10
10
 
11
11
 
12
- == Usage
12
+ == Basic Usage
13
13
 
14
- require 'rack-ssl-enforcer'
14
+ require 'rack/ssl-enforcer'
15
15
  use Rack::SslEnforcer
16
16
 
17
- This will redirect all requests to SSL. Rack::SslEnforcer accepts params:
17
+ Or, if you are using Bundler, just add this to your Gemfile:
18
+
19
+ gem 'rack-ssl-enforcer'
20
+
21
+ To use Rack::SslEnforcer in your Rails application, add the following line to your application
22
+ config file (config/application.rb for Rails3, config/environment.rb for Rails2):
23
+
24
+ config.middleware.use Rack::SslEnforcer
25
+
26
+ If all you want is SSL for your whole application, you are done! However, you can specify some
27
+
28
+
29
+ == Options
18
30
 
19
31
  You might need the :redirect_to option if the requested URL can't be determined (e.g. if using a proxy).
20
32
 
21
- use Rack::SslEnforcer, :redirect_to => 'https://example.org'
22
-
23
- You can also define specific regex patterns or paths to redirect.
24
-
25
- use Rack::SslEnforcer, :only => /^\/admin\//
26
- use Rack::SslEnforcer, :only => "/login"
27
- use Rack::SslEnforcer, :only => ["/login", /\.xml$/]
28
-
29
- And force http for non-https path
33
+ config.middleware.use Rack::SslEnforcer, :redirect_to => 'https://example.org'
34
+
35
+ You can also define specific regex patterns or paths or hosts to redirect.
30
36
 
31
- use Rack::SslEnforcer, :only => ["/login", /\.xml$/], :strict => true
37
+ config.middleware.use Rack::SslEnforcer, :only => /^\/admin\//
38
+ config.middleware.use Rack::SslEnforcer, :only => "/login"
39
+ config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/]
40
+ config.middleware.use Rack::SslEnforcer, :only_hosts => 'api.example.com'
41
+ config.middleware.use Rack::SslEnforcer, :only_hosts => ["[www|api]\.example\.org", 'example.com']
42
+ config.middleware.use Rack::SslEnforcer, :except_hosts => 'help.example.com'
43
+ config.middleware.use Rack::SslEnforcer, :except_hosts => /[help|blog]\.example\.com$/
32
44
 
33
- To set HSTS expiry and subdomain inclusion (defaults: one year, true)
45
+ Note: hosts options take precedence over the path options. See tests for examples.
34
46
 
35
- use Rack::SslEnforcer, :hsts => {:expires => 500, :subdomains => false}
47
+ Use the :strict option to force http for all requests not matching your :only specification
48
+
49
+ config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/], :strict => true
50
+ config.middleware.use Rack::SslEnforcer, :only_hosts => 'api.example.com', :strict => true
51
+
52
+ Or in the case where you have matching urls with different methods (rails restful routes: get#users post#users || get#user/:id put#user/:id) you may need to post and put to secure but redirect to http on get.
53
+
54
+ config.middleware.use Rack::SslEnforcer, :only => [/^\/users\/(.+)\/edit/], :mixed => true
55
+
56
+ The above will allow you to post/put from the secure/non-secure urls keeping the original schema.
57
+
58
+ To set HSTS expiry and subdomain inclusion (defaults: one year, true). Strict option disables HSTS.
59
+
60
+ config.middleware.use Rack::SslEnforcer, :hsts => { :expires => 500, :subdomains => false }
61
+ config.middleware.use Rack::SslEnforcer, :hsts => true # equivalent to { :expires => 31536000, :subdomains => true }
36
62
 
37
63
 
38
64
  == TODO
@@ -47,21 +73,22 @@ To set HSTS expiry and subdomain inclusion (defaults: one year, true)
47
73
  * {Rémy Coutable}[http://github.com/rymai]
48
74
  * {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
49
75
  * {Paul Annesley}[https://github.com/pda]
76
+ * {Saimon Moore}[https://github.com/saimonmoore]
50
77
 
51
78
 
52
79
  == Credits
53
80
 
54
- Flagging cookies as secure functionality is greatly inspired by {Joshua Peek's Rack::SSL}[https://github.com/josh/rack-ssl]
81
+ Flagging cookies as secure functionality and HSTS support is greatly inspired by {Joshua Peek's Rack::SSL}[https://github.com/josh/rack-ssl]
55
82
 
56
83
 
57
84
  == Note on Patches/Pull Requests
58
-
85
+
59
86
  * Fork the project.
60
87
  * Make your feature addition or bug fix.
61
88
  * Add tests for it. This is important so I don't break it in a
62
89
  future version unintentionally.
63
90
  * Commit, do not mess with rakefile, version, or history.
64
- (if you want to have your own version,
91
+ (if you want to have your own version,
65
92
  that is fine but bump version in a commit by itself I can ignore when I pull)
66
93
  * Send me a pull request. Bonus points for topic branches.
67
94
 
@@ -0,0 +1 @@
1
+ require 'rack/ssl-enforcer'
@@ -1,39 +1,42 @@
1
1
  module Rack
2
2
  class SslEnforcer
3
-
3
+
4
4
  def initialize(app, options = {})
5
5
  @app, @options = app, options
6
6
  end
7
-
7
+
8
8
  def call(env)
9
9
  @req = Rack::Request.new(env)
10
- if enforce_ssl?(env)
10
+ if enforce_ssl?(@req)
11
11
  scheme = 'https' unless ssl_request?(env)
12
- elsif ssl_request?(env) && @options[:strict]
12
+ elsif ssl_request?(env) && enforcement_non_ssl?(env)
13
13
  scheme = 'http'
14
14
  end
15
-
15
+
16
16
  if scheme
17
- location = @options[:redirect_to] || replace_scheme(@req, scheme).url
17
+ location = replace_scheme(@req, scheme).url
18
18
  body = "<html><body>You are being <a href=\"#{location}\">redirected</a>.</body></html>"
19
19
  [301, { 'Content-Type' => 'text/html', 'Location' => location }, [body]]
20
20
  elsif ssl_request?(env)
21
21
  status, headers, body = @app.call(env)
22
22
  flag_cookies_as_secure!(headers)
23
- set_hsts_headers!(headers)
23
+ set_hsts_headers!(headers) if @options[:hsts] && !@options[:strict]
24
24
  [status, headers, body]
25
25
  else
26
26
  @app.call(env)
27
27
  end
28
28
  end
29
-
30
-
29
+
31
30
  private
32
-
31
+
32
+ def enforcement_non_ssl?(env)
33
+ true if @options[:strict] || @options[:mixed] && !(env['REQUEST_METHOD'] == 'PUT' || env['REQUEST_METHOD'] == 'POST')
34
+ end
35
+
33
36
  def ssl_request?(env)
34
37
  scheme(env) == 'https'
35
38
  end
36
-
39
+
37
40
  # Fixed in rack >= 1.3
38
41
  def scheme(env)
39
42
  if env['HTTPS'] == 'on'
@@ -44,29 +47,81 @@ module Rack
44
47
  env['rack.url_scheme']
45
48
  end
46
49
  end
47
-
48
- def enforce_ssl?(env)
49
- if @options[:only]
50
- rules = [@options[:only]].flatten
51
- rules.any? do |pattern|
52
- if pattern.is_a?(Regexp)
53
- @req.path =~ pattern
50
+
51
+ def matches?(key, pattern, req)
52
+ if pattern.is_a?(Regexp)
53
+ case key
54
+ when :only
55
+ req.path =~ pattern
56
+ when :except
57
+ req.path !~ pattern
58
+ when :only_hosts
59
+ req.host =~ pattern
60
+ when :except_hosts
61
+ req.host !~ pattern
62
+ end
63
+ else
64
+ case key
65
+ when :only
66
+ req.path[0,pattern.length] == pattern
67
+ when :except
68
+ req.path[0,pattern.length] != pattern
69
+ when :only_hosts
70
+ req.host == pattern
71
+ when :except_hosts
72
+ req.host != pattern
73
+ end
74
+ end
75
+ end
76
+
77
+ def enforce_ssl_for?(keys, req)
78
+ if keys.any? {|option| @options.key?(option)}
79
+ keys.any? do |key|
80
+ rules = [@options[key]].flatten.compact
81
+ rules.any? do |pattern|
82
+ matches?(key, pattern, req)
83
+ end
84
+ end
85
+ else
86
+ false
87
+ end
88
+ end
89
+
90
+ def enforce_ssl?(req)
91
+ path_keys = [:only, :except]
92
+ hosts_keys = [:only_hosts, :except_hosts]
93
+ if hosts_keys.any? {|option| @options.key?(option)}
94
+ if enforce_ssl_for?(hosts_keys, req)
95
+ if path_keys.any? {|option| @options.key?(option)}
96
+ enforce_ssl_for?(path_keys, req)
54
97
  else
55
- @req.path[0,pattern.length] == pattern
98
+ true
56
99
  end
100
+ else
101
+ false
57
102
  end
103
+ elsif path_keys.any? {|option| @options.key?(option)}
104
+ enforce_ssl_for?(path_keys, req)
58
105
  else
59
106
  true
60
107
  end
61
108
  end
62
-
109
+
63
110
  def replace_scheme(req, scheme)
111
+ if @options[:redirect_to]
112
+ uri = URI.split(@options[:redirect_to])
113
+ uri = uri[2] || uri[5]
114
+ else
115
+ uri = nil
116
+ end
64
117
  Rack::Request.new(req.env.merge(
65
118
  'rack.url_scheme' => scheme,
119
+ 'HTTP_X_FORWARDED_PROTO' => scheme,
120
+ 'HTTP_X_FORWARDED_PORT' => port_for(scheme).to_s,
66
121
  'SERVER_PORT' => port_for(scheme).to_s
67
- ))
122
+ ).merge(uri ? {'HTTP_HOST' => uri} : {}))
68
123
  end
69
-
124
+
70
125
  def port_for(scheme)
71
126
  scheme == 'https' ? 443 : 80
72
127
  end
@@ -83,7 +138,7 @@ module Rack
83
138
  }.join("\n")
84
139
  end
85
140
  end
86
-
141
+
87
142
  # see http://en.wikipedia.org/wiki/Strict_Transport_Security
88
143
  def set_hsts_headers!(headers)
89
144
  opts = { :expires => 31536000, :subdomains => true }.merge(@options[:hsts] || {})
@@ -91,6 +146,6 @@ module Rack
91
146
  value += "; includeSubDomains" if opts[:subdomains]
92
147
  headers.merge!({ 'Strict-Transport-Security' => value })
93
148
  end
94
-
149
+
95
150
  end
96
- end
151
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class SslEnforcer
3
- VERSION = "0.2.0"
3
+ VERSION = "0.2.1"
4
4
  end
5
- end
5
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-ssl-enforcer
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 21
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
8
  - 2
8
- - 0
9
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Tobias Matthies
@@ -15,7 +16,7 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2010-11-17 00:00:00 +01:00
19
+ date: 2011-02-15 00:00:00 +01:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
@@ -24,13 +25,14 @@ dependencies:
24
25
  requirement: &id001 !ruby/object:Gem::Requirement
25
26
  none: false
26
27
  requirements:
27
- - - ">="
28
+ - - ~>
28
29
  - !ruby/object:Gem::Version
30
+ hash: 19
29
31
  segments:
30
32
  - 0
31
- - 2
32
- - 1
33
- version: 0.2.1
33
+ - 3
34
+ - 0
35
+ version: 0.3.0
34
36
  type: :development
35
37
  version_requirements: *id001
36
38
  - !ruby/object:Gem::Dependency
@@ -41,101 +43,94 @@ dependencies:
41
43
  requirements:
42
44
  - - ~>
43
45
  - !ruby/object:Gem::Version
46
+ hash: 23
44
47
  segments:
45
48
  - 0
46
49
  - 1
47
- - 3
48
- version: 0.1.3
50
+ - 6
51
+ version: 0.1.6
49
52
  type: :development
50
53
  version_requirements: *id002
51
- - !ruby/object:Gem::Dependency
52
- name: guard-bundler
53
- prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
55
- none: false
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- segments:
60
- - 0
61
- version: "0"
62
- type: :development
63
- version_requirements: *id003
64
54
  - !ruby/object:Gem::Dependency
65
55
  name: bundler
66
56
  prerelease: false
67
- requirement: &id004 !ruby/object:Gem::Requirement
57
+ requirement: &id003 !ruby/object:Gem::Requirement
68
58
  none: false
69
59
  requirements:
70
60
  - - ~>
71
61
  - !ruby/object:Gem::Version
62
+ hash: 3
72
63
  segments:
73
64
  - 1
74
65
  - 0
75
- - 5
76
- version: 1.0.5
66
+ - 10
67
+ version: 1.0.10
77
68
  type: :development
78
- version_requirements: *id004
69
+ version_requirements: *id003
79
70
  - !ruby/object:Gem::Dependency
80
71
  name: test-unit
81
72
  prerelease: false
82
- requirement: &id005 !ruby/object:Gem::Requirement
73
+ requirement: &id004 !ruby/object:Gem::Requirement
83
74
  none: false
84
75
  requirements:
85
76
  - - ~>
86
77
  - !ruby/object:Gem::Version
78
+ hash: 9
87
79
  segments:
88
80
  - 2
89
81
  - 1
90
82
  - 1
91
83
  version: 2.1.1
92
84
  type: :development
93
- version_requirements: *id005
85
+ version_requirements: *id004
94
86
  - !ruby/object:Gem::Dependency
95
87
  name: shoulda
96
88
  prerelease: false
97
- requirement: &id006 !ruby/object:Gem::Requirement
89
+ requirement: &id005 !ruby/object:Gem::Requirement
98
90
  none: false
99
91
  requirements:
100
92
  - - ~>
101
93
  - !ruby/object:Gem::Version
94
+ hash: 37
102
95
  segments:
103
96
  - 2
104
97
  - 11
105
98
  - 3
106
99
  version: 2.11.3
107
100
  type: :development
108
- version_requirements: *id006
101
+ version_requirements: *id005
109
102
  - !ruby/object:Gem::Dependency
110
103
  name: rack
111
104
  prerelease: false
112
- requirement: &id007 !ruby/object:Gem::Requirement
105
+ requirement: &id006 !ruby/object:Gem::Requirement
113
106
  none: false
114
107
  requirements:
115
108
  - - ~>
116
109
  - !ruby/object:Gem::Version
110
+ hash: 31
117
111
  segments:
118
112
  - 1
119
113
  - 2
120
114
  - 0
121
115
  version: 1.2.0
122
116
  type: :development
123
- version_requirements: *id007
117
+ version_requirements: *id006
124
118
  - !ruby/object:Gem::Dependency
125
119
  name: rack-test
126
120
  prerelease: false
127
- requirement: &id008 !ruby/object:Gem::Requirement
121
+ requirement: &id007 !ruby/object:Gem::Requirement
128
122
  none: false
129
123
  requirements:
130
124
  - - ~>
131
125
  - !ruby/object:Gem::Version
126
+ hash: 3
132
127
  segments:
133
128
  - 0
134
129
  - 5
135
130
  - 4
136
131
  version: 0.5.4
137
132
  type: :development
138
- version_requirements: *id008
133
+ version_requirements: *id007
139
134
  description: Rack::SslEnforcer is a simple Rack middleware to enforce ssl connections
140
135
  email:
141
136
  - tm@mit2m.de
@@ -147,9 +142,9 @@ extensions: []
147
142
  extra_rdoc_files: []
148
143
 
149
144
  files:
150
- - lib/rack/rack-ssl-enforcer.rb
151
145
  - lib/rack/ssl-enforcer/version.rb
152
146
  - lib/rack/ssl-enforcer.rb
147
+ - lib/rack-ssl-enforcer.rb
153
148
  - LICENSE
154
149
  - README.rdoc
155
150
  has_rdoc: true
@@ -166,6 +161,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
166
161
  requirements:
167
162
  - - ">="
168
163
  - !ruby/object:Gem::Version
164
+ hash: 3
169
165
  segments:
170
166
  - 0
171
167
  version: "0"
@@ -174,6 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
170
  requirements:
175
171
  - - ">="
176
172
  - !ruby/object:Gem::Version
173
+ hash: 23
177
174
  segments:
178
175
  - 1
179
176
  - 3
@@ -182,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
182
179
  requirements: []
183
180
 
184
181
  rubyforge_project: rack-ssl-enforcer
185
- rubygems_version: 1.3.7
182
+ rubygems_version: 1.5.0
186
183
  signing_key:
187
184
  specification_version: 3
188
185
  summary: A simple Rack middleware to enforce SSL
@@ -1 +0,0 @@
1
- require 'rack/ssl-enforcer'