rack-ssl-enforcer 0.2.4 → 0.2.5
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.
- data/LICENSE +1 -1
- data/README.md +220 -0
- data/lib/rack/ssl-enforcer.rb +112 -96
- data/lib/rack/ssl-enforcer/constraint.rb +38 -0
- data/lib/rack/ssl-enforcer/version.rb +1 -1
- metadata +15 -8
- data/README.rdoc +0 -111
data/LICENSE
CHANGED
data/README.md
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
# Rack::SslEnforcer [](http://travis-ci.org/tobmatth/rack-ssl-enforcer)
|
2
|
+
|
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 by default (HSTS must be set manually).
|
5
|
+
|
6
|
+
Tested against Ruby 1.8.7, 1.9.2, 1.9.3, ruby-head, REE and the latest versions of Rubinius & JRuby.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
The simplest way to install Rack::SslEnforcer is to use [Bundler](http://gembundler.com/).
|
11
|
+
|
12
|
+
Add Rack::SslEnforcer to your `Gemfile`:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'rack-ssl-enforcer'
|
16
|
+
```
|
17
|
+
|
18
|
+
### Installing on Sinatra / Padrino application
|
19
|
+
|
20
|
+
In order for Rack::SslEnforcer to properly work it has to be at the top
|
21
|
+
of the Rack Middleware.
|
22
|
+
|
23
|
+
Using `enable :session` will place Rack::Session::Cookie before Rack::Ssl::Enforcer
|
24
|
+
and will prevent Rack::Ssl::Enforcer from marking cookies as secure.
|
25
|
+
|
26
|
+
To fix this issue do not use `enable :sessions` instead add the
|
27
|
+
Rack::Session::Cookie middleware after Rack::Ssl::Enforcer.
|
28
|
+
|
29
|
+
Eg:
|
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
|
+
|
40
|
+
|
41
|
+
## Basic Usage
|
42
|
+
|
43
|
+
If you don't use Bundler, be sure to require Rack::SslEnforcer manually before actually using the middleware:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'rack/ssl-enforcer'
|
47
|
+
use Rack::SslEnforcer
|
48
|
+
```
|
49
|
+
|
50
|
+
To use Rack::SslEnforcer in your Rails application, add the following line to your application config file (`config/application.rb` for Rails 3, `config/environment.rb` for Rails 2):
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
config.middleware.use Rack::SslEnforcer
|
54
|
+
```
|
55
|
+
|
56
|
+
If all you want is SSL for your whole application, you are done! Otherwise, you can specify some options described below.
|
57
|
+
|
58
|
+
## Options
|
59
|
+
|
60
|
+
### Host constraints
|
61
|
+
|
62
|
+
You can enforce SSL connections only for certain hosts with `:only_hosts`, or prevent certain hosts from being forced to SSL with `:except_hosts`. Constraints can be a `String`, a `Regex` or an array of `String` or `Regex` (possibly mixed), as shown in the following examples:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
config.middleware.use Rack::SslEnforcer, :only_hosts => 'api.example.com'
|
66
|
+
# Please note that, for instance, both http://help.example.com/demo and https://help.example.com/demo would be accessible here
|
67
|
+
|
68
|
+
config.middleware.use Rack::SslEnforcer, :except_hosts => /[help|blog]\.example\.com$/
|
69
|
+
|
70
|
+
config.middleware.use Rack::SslEnforcer, :only_hosts => [/[secure|admin]\.example\.org$/, 'api.example.com']
|
71
|
+
```
|
72
|
+
|
73
|
+
### Path constraints
|
74
|
+
|
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
|
+
|
77
|
+
```ruby
|
78
|
+
config.middleware.use Rack::SslEnforcer, :only => '/login'
|
79
|
+
# Please note that, for instance, both http://example.com/demo and https://example.com/demo would be accessible here
|
80
|
+
|
81
|
+
config.middleware.use Rack::SslEnforcer, :only => %r{^/admin/}
|
82
|
+
|
83
|
+
config.middleware.use Rack::SslEnforcer, :except => ['/demo', %r{^/public/}]
|
84
|
+
```
|
85
|
+
|
86
|
+
### Method constraints
|
87
|
+
|
88
|
+
You can enforce SSL connections only for certain HTTP methods with `:only_methods`, or prevent certain HTTP methods from being forced to SSL with `:except_methods`. Constraints can be a `String` or an array of `String`, as shown in the following examples:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# constraint as a String
|
92
|
+
config.middleware.use Rack::SslEnforcer, :only_methods => 'POST'
|
93
|
+
# Please note that, for instance, GET requests would be accessible via SSL and non-SSL connection here
|
94
|
+
|
95
|
+
config.middleware.use Rack::SslEnforcer, :except_methods => ['GET', 'HEAD']
|
96
|
+
```
|
97
|
+
|
98
|
+
Note: The `:hosts` constraint takes precedence over the `:path` constraint. Please see the tests for examples.
|
99
|
+
|
100
|
+
### Force-redirection to non-SSL connection if constraint is not matched
|
101
|
+
|
102
|
+
Use the `:strict` option to force non-SSL connection for all requests not matching the constraints you set. Examples:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/], :strict => true
|
106
|
+
# https://example.com/demo would be redirected to http://example.com/demo
|
107
|
+
|
108
|
+
config.middleware.use Rack::SslEnforcer, :except_hosts => 'demo.example.com', :strict => true
|
109
|
+
# https://demo.example.com would be redirected to http://demo.example.com
|
110
|
+
```
|
111
|
+
|
112
|
+
### Automatic method constraints
|
113
|
+
|
114
|
+
In the case where you have matching URLs with different HTTP methods – for instance Rails RESTful routes: `GET /users`, `POST /users`, `GET /user/:id` and `PUT /user/:id` – you may need to force POST and PUT requests to SSL connection but redirect to non-SSL connection on GET.
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
config.middleware.use Rack::SslEnforcer, :only => [%r{^/users/}], :mixed => true
|
118
|
+
```
|
119
|
+
|
120
|
+
The above will allow you to POST/PUT from the secure/non-secure URLs keeping the original schema.
|
121
|
+
|
122
|
+
### HTTP Strict Transport Security (HSTS)
|
123
|
+
|
124
|
+
To set HSTS expiry and subdomain inclusion (defaults respectively to `one year` and `true`).
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
config.middleware.use Rack::SslEnforcer, :hsts => { :expires => 500, :subdomains => false }
|
128
|
+
config.middleware.use Rack::SslEnforcer, :hsts => true # equivalent to { :expires => 31536000, :subdomains => true }
|
129
|
+
```
|
130
|
+
Please note that the strict option disables HSTS.
|
131
|
+
|
132
|
+
### Redirect to specific URL (e.g. if you're using a proxy)
|
133
|
+
|
134
|
+
You might need the `:redirect_to` option if the requested URL can't be determined.
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
config.middleware.use Rack::SslEnforcer, :redirect_to => 'https://example.org'
|
138
|
+
```
|
139
|
+
|
140
|
+
### Custom HTTP port
|
141
|
+
|
142
|
+
If you're using a different port than the default (80) for HTTP, you can specify it with the `:http_port` option:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
config.middleware.use Rack::SslEnforcer, :http_port => 8080
|
146
|
+
```
|
147
|
+
|
148
|
+
### Custom HTTPS port
|
149
|
+
|
150
|
+
If you're using a different port than the default (443) for HTTPS, you can specify it with the `:https_port` option:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
config.middleware.use Rack::SslEnforcer, :https_port => 444
|
154
|
+
```
|
155
|
+
|
156
|
+
### Secure cookies disabling
|
157
|
+
|
158
|
+
Finally you might want to share a cookie based session between HTTP and HTTPS.
|
159
|
+
This is not possible by default with Rack::SslEnforcer for [security reasons](http://en.wikipedia.org/wiki/HTTP_cookie#Cookie_theft_and_session_hijacking).
|
160
|
+
|
161
|
+
Nevertheless, you can set the `:force_secure_cookies` option to `false` in order to be able to share a cookie based session between HTTP and HTTPS:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
config.middleware.use Rack::SslEnforcer, :only => "/login", :force_secure_cookies => false
|
165
|
+
```
|
166
|
+
|
167
|
+
But be aware that if you do so, you have to make sure that the content of you cookie is encoded.
|
168
|
+
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
|
+
|
170
|
+
## Deployment
|
171
|
+
|
172
|
+
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.
|
173
|
+
|
174
|
+
The reason this happens is that Rack::SslEnforcer can't detect if you are running SSL or not. The solution is to have your front-end server send extra headers for Rack::SslEnforcer to identify the request protocol.
|
175
|
+
|
176
|
+
### Nginx
|
177
|
+
|
178
|
+
In the `location` block for your app's SSL configuration, include the following proxy header configuration:
|
179
|
+
|
180
|
+
`proxy_set_header X-Forwarded-Proto https;`
|
181
|
+
|
182
|
+
### Passenger
|
183
|
+
|
184
|
+
Or, if you're using mod_rails/passenger (which will ignore the proxy_xxx directives):
|
185
|
+
|
186
|
+
`passenger_set_cgi_param HTTP_X_FORWARDED_PROTO https;`
|
187
|
+
|
188
|
+
If you're sharing a single `server` block for http AND https access you can add:
|
189
|
+
|
190
|
+
`passenger_set_cgi_param HTTP_X_FORWARDED_PROTO $scheme;`
|
191
|
+
|
192
|
+
This makes sure that Rack::SslEnforcer knows it's being accessed over SSL. Just restart Nginx for these changes to take effect.
|
193
|
+
|
194
|
+
## TODO
|
195
|
+
|
196
|
+
* Cleanup tests
|
197
|
+
|
198
|
+
## Contributors
|
199
|
+
|
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)
|
205
|
+
|
206
|
+
## Credits
|
207
|
+
|
208
|
+
Flagging cookies as secure functionality and HSTS support is greatly inspired by [Joshua Peek's Rack::SSL](https://github.com/josh/rack-ssl).
|
209
|
+
|
210
|
+
## Note on Patches / Pull Requests
|
211
|
+
|
212
|
+
* Fork the project.
|
213
|
+
* Code your feature addition or bug fix.
|
214
|
+
* **Add tests for it.** This is important so we don't break it in a future version unintentionally.
|
215
|
+
* Commit, do not mess with Rakefile or version number. If you want to have your own version, that's fine but bump version in a commit by itself so we can ignore it when merging.
|
216
|
+
* Send a pull request. Bonus points for topic branches.
|
217
|
+
|
218
|
+
## Copyright
|
219
|
+
|
220
|
+
Copyright (c) 2010-2012 Tobias Matthies. See LICENSE for details.
|
data/lib/rack/ssl-enforcer.rb
CHANGED
@@ -1,38 +1,46 @@
|
|
1
|
+
require 'rack/ssl-enforcer/constraint'
|
2
|
+
|
1
3
|
module Rack
|
4
|
+
|
2
5
|
class SslEnforcer
|
3
6
|
|
7
|
+
CONSTRAINTS_BY_TYPE = {
|
8
|
+
:hosts => [:only_hosts, :except_hosts],
|
9
|
+
:path => [:only, :except],
|
10
|
+
:methods => [:only_methods, :except_methods]
|
11
|
+
}
|
12
|
+
|
4
13
|
# Warning: If you set the option force_secure_cookies to false, make sure that your cookies
|
5
|
-
#
|
14
|
+
# are encoded and that you understand the consequences (see documentation)
|
6
15
|
def initialize(app, options={})
|
7
16
|
default_options = {
|
8
|
-
:redirect_to
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:mixed => false,
|
15
|
-
:hsts => nil,
|
16
|
-
:http_port => nil,
|
17
|
-
:https_port => nil,
|
17
|
+
:redirect_to => nil,
|
18
|
+
:strict => false,
|
19
|
+
:mixed => false,
|
20
|
+
:hsts => nil,
|
21
|
+
:http_port => nil,
|
22
|
+
:https_port => nil,
|
18
23
|
:force_secure_cookies => true
|
19
24
|
}
|
25
|
+
CONSTRAINTS_BY_TYPE.values.each { |constraint| default_options[constraint] = nil }
|
26
|
+
|
20
27
|
@app, @options = app, default_options.merge(options)
|
21
28
|
end
|
22
29
|
|
23
30
|
def call(env)
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
31
|
+
@request = Rack::Request.new(env)
|
32
|
+
|
33
|
+
return @app.call(env) if ignore?
|
34
|
+
|
35
|
+
@scheme = if enforce_ssl?
|
36
|
+
'https'
|
37
|
+
elsif enforce_non_ssl?
|
38
|
+
'http'
|
29
39
|
end
|
30
40
|
|
31
|
-
if
|
32
|
-
|
33
|
-
|
34
|
-
[301, { 'Content-Type' => 'text/html', 'Location' => location }, [body]]
|
35
|
-
elsif ssl_request?(env)
|
41
|
+
if redirect_required?
|
42
|
+
modify_location_and_redirect
|
43
|
+
elsif ssl_request?
|
36
44
|
status, headers, body = @app.call(env)
|
37
45
|
flag_cookies_as_secure!(headers) if @options[:force_secure_cookies]
|
38
46
|
set_hsts_headers!(headers) if @options[:hsts] && !@options[:strict]
|
@@ -43,105 +51,113 @@ module Rack
|
|
43
51
|
end
|
44
52
|
|
45
53
|
private
|
54
|
+
|
55
|
+
def redirect_required?
|
56
|
+
scheme_mismatch? || host_mismatch?
|
57
|
+
end
|
46
58
|
|
47
|
-
def
|
48
|
-
|
59
|
+
def ignore?
|
60
|
+
if @options[:ignore]
|
61
|
+
rules = [@options[:ignore]].flatten.compact
|
62
|
+
rules.any? do |rule|
|
63
|
+
SslEnforcerConstraint.new(:ignore, rule, @request).matches?
|
64
|
+
end
|
65
|
+
else
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def scheme_mismatch?
|
71
|
+
@scheme && @scheme != current_scheme
|
72
|
+
end
|
73
|
+
|
74
|
+
def host_mismatch?
|
75
|
+
destination_host && destination_host != @request.host
|
76
|
+
end
|
77
|
+
|
78
|
+
def modify_location_and_redirect
|
79
|
+
location = "#{current_scheme}://#{@request.host}#{@request.fullpath}"
|
80
|
+
location = replace_scheme(location, @scheme)
|
81
|
+
location = replace_host(location, @options[:redirect_to])
|
82
|
+
redirect_to(location)
|
83
|
+
end
|
84
|
+
|
85
|
+
def redirect_to(location)
|
86
|
+
body = "<html><body>You are being <a href=\"#{location}\">redirected</a>.</body></html>"
|
87
|
+
[301, { 'Content-Type' => 'text/html', 'Location' => location }, [body]]
|
49
88
|
end
|
50
89
|
|
51
|
-
def ssl_request?
|
52
|
-
|
90
|
+
def ssl_request?
|
91
|
+
current_scheme == 'https'
|
92
|
+
end
|
93
|
+
|
94
|
+
def destination_host
|
95
|
+
if @options[:redirect_to]
|
96
|
+
host_parts = URI.split(@options[:redirect_to])
|
97
|
+
host_parts[2] || host_parts[5]
|
98
|
+
end
|
53
99
|
end
|
54
100
|
|
55
101
|
# Fixed in rack >= 1.3
|
56
|
-
def
|
57
|
-
if env['HTTPS'] == 'on'
|
102
|
+
def current_scheme
|
103
|
+
if @request.env['HTTPS'] == 'on'
|
58
104
|
'https'
|
59
|
-
elsif env['HTTP_X_FORWARDED_PROTO']
|
60
|
-
env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
|
105
|
+
elsif @request.env['HTTP_X_FORWARDED_PROTO']
|
106
|
+
@request.env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
|
61
107
|
else
|
62
|
-
|
108
|
+
@request.scheme
|
63
109
|
end
|
64
110
|
end
|
65
111
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
req.path =~ pattern
|
71
|
-
when :except
|
72
|
-
req.path !~ pattern
|
73
|
-
when :only_hosts
|
74
|
-
req.host =~ pattern
|
75
|
-
when :except_hosts
|
76
|
-
req.host !~ pattern
|
77
|
-
end
|
112
|
+
def enforce_ssl_for?(keys)
|
113
|
+
provided_keys = keys.select { |key| @options[key] }
|
114
|
+
if provided_keys.empty?
|
115
|
+
true
|
78
116
|
else
|
79
|
-
|
80
|
-
when :only
|
81
|
-
req.path[0,pattern.length] == pattern
|
82
|
-
when :except
|
83
|
-
req.path[0,pattern.length] != pattern
|
84
|
-
when :only_hosts
|
85
|
-
req.host == pattern
|
86
|
-
when :except_hosts
|
87
|
-
req.host != pattern
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def enforce_ssl_for?(keys, req)
|
93
|
-
if keys.any? { |option| @options[option] }
|
94
|
-
keys.any? do |key|
|
117
|
+
provided_keys.all? do |key|
|
95
118
|
rules = [@options[key]].flatten.compact
|
96
|
-
|
97
|
-
|
98
|
-
matches?(key, pattern, req)
|
99
|
-
end
|
119
|
+
rules.send([:except_hosts, :except].include?(key) ? :all? : :any?) do |rule|
|
120
|
+
SslEnforcerConstraint.new(key, rule, @request).matches?
|
100
121
|
end
|
101
122
|
end
|
102
|
-
else
|
103
|
-
false
|
104
123
|
end
|
105
124
|
end
|
106
125
|
|
107
|
-
def
|
108
|
-
|
109
|
-
hosts_keys = [:only_hosts, :except_hosts]
|
110
|
-
if hosts_keys.any? { |option| @options[option] }
|
111
|
-
if enforce_ssl_for?(hosts_keys, req)
|
112
|
-
if path_keys.any? { |option| @options[option] }
|
113
|
-
enforce_ssl_for?(path_keys, req)
|
114
|
-
else
|
115
|
-
true
|
116
|
-
end
|
117
|
-
else
|
118
|
-
false
|
119
|
-
end
|
120
|
-
elsif path_keys.any? { |option| @options[option] }
|
121
|
-
enforce_ssl_for?(path_keys, req)
|
122
|
-
else
|
123
|
-
true
|
124
|
-
end
|
126
|
+
def enforce_non_ssl?
|
127
|
+
@options[:strict] || @options[:mixed] && !(@request.request_method == 'PUT' || @request.request_method == 'POST')
|
125
128
|
end
|
126
129
|
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
uri = uri[2] || uri[5]
|
131
|
-
else
|
132
|
-
uri = nil
|
130
|
+
def enforce_ssl?
|
131
|
+
CONSTRAINTS_BY_TYPE.inject(true) do |memo, (type, keys)|
|
132
|
+
memo && enforce_ssl_for?(keys)
|
133
133
|
end
|
134
|
-
host = uri || req.host
|
135
|
-
port = port_for(scheme).to_s
|
136
|
-
|
137
|
-
URI.parse("#{scheme}://#{host}:#{port}#{req.fullpath}").to_s
|
138
134
|
end
|
139
135
|
|
140
|
-
def
|
136
|
+
def replace_scheme(uri, scheme)
|
137
|
+
return uri if not scheme_mismatch?
|
138
|
+
|
139
|
+
port = adjust_port_to(scheme)
|
140
|
+
uri_parts = URI.split(uri)
|
141
|
+
uri_parts[3] = port unless port.nil?
|
142
|
+
uri_parts[0] = scheme
|
143
|
+
URI::HTTP.new(*uri_parts).to_s
|
144
|
+
end
|
145
|
+
|
146
|
+
def replace_host(uri, host)
|
147
|
+
return uri unless host_mismatch?
|
148
|
+
|
149
|
+
host_parts = URI.split(host)
|
150
|
+
new_host = host_parts[2] || host_parts[5]
|
151
|
+
uri_parts = URI.split(uri)
|
152
|
+
uri_parts[2] = new_host
|
153
|
+
URI::HTTPS.new(*uri_parts).to_s
|
154
|
+
end
|
155
|
+
|
156
|
+
def adjust_port_to(scheme)
|
141
157
|
if scheme == 'https'
|
142
|
-
@options[:https_port]
|
143
|
-
|
144
|
-
@options[:http_port]
|
158
|
+
@options[:https_port] if @options[:https_port] && @options[:https_port] != URI::HTTPS.default_port
|
159
|
+
elsif scheme == 'http'
|
160
|
+
@options[:http_port] if @options[:http_port] && @options[:http_port] != URI::HTTP.default_port
|
145
161
|
end
|
146
162
|
end
|
147
163
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class SslEnforcerConstraint
|
2
|
+
def initialize(name, rule, request)
|
3
|
+
@name = name
|
4
|
+
@rule = rule
|
5
|
+
@request = request
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches?
|
9
|
+
if @rule.is_a?(String) && [:only, :except].include?(@name)
|
10
|
+
result = tested_string[0, @rule.size].send(operator, @rule)
|
11
|
+
else
|
12
|
+
result = tested_string.send(operator, @rule)
|
13
|
+
end
|
14
|
+
|
15
|
+
negate_result? ? !result : result
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def negate_result?
|
21
|
+
@name.to_s =~ /except/
|
22
|
+
end
|
23
|
+
|
24
|
+
def operator
|
25
|
+
@rule.is_a?(Regexp) ? "=~" : "=="
|
26
|
+
end
|
27
|
+
|
28
|
+
def tested_string
|
29
|
+
case @name.to_s
|
30
|
+
when /hosts/
|
31
|
+
@request.host
|
32
|
+
when /methods/
|
33
|
+
@request.request_method
|
34
|
+
else
|
35
|
+
@request.path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
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
|
-
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
9
|
+
- 5
|
10
|
+
version: 0.2.5
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Tobias Matthies
|
@@ -15,8 +16,7 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
-
default_executable:
|
19
|
+
date: 2012-11-14 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: bundler
|
@@ -26,6 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 15
|
29
30
|
segments:
|
30
31
|
- 1
|
31
32
|
- 0
|
@@ -40,6 +41,7 @@ dependencies:
|
|
40
41
|
requirements:
|
41
42
|
- - ~>
|
42
43
|
- !ruby/object:Gem::Version
|
44
|
+
hash: 5
|
43
45
|
segments:
|
44
46
|
- 2
|
45
47
|
- 3
|
@@ -54,6 +56,7 @@ dependencies:
|
|
54
56
|
requirements:
|
55
57
|
- - ~>
|
56
58
|
- !ruby/object:Gem::Version
|
59
|
+
hash: 37
|
57
60
|
segments:
|
58
61
|
- 2
|
59
62
|
- 11
|
@@ -69,6 +72,7 @@ dependencies:
|
|
69
72
|
requirements:
|
70
73
|
- - ~>
|
71
74
|
- !ruby/object:Gem::Version
|
75
|
+
hash: 31
|
72
76
|
segments:
|
73
77
|
- 1
|
74
78
|
- 2
|
@@ -84,6 +88,7 @@ dependencies:
|
|
84
88
|
requirements:
|
85
89
|
- - ~>
|
86
90
|
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
87
92
|
segments:
|
88
93
|
- 0
|
89
94
|
- 5
|
@@ -102,12 +107,12 @@ extensions: []
|
|
102
107
|
extra_rdoc_files: []
|
103
108
|
|
104
109
|
files:
|
110
|
+
- lib/rack/ssl-enforcer/constraint.rb
|
105
111
|
- lib/rack/ssl-enforcer/version.rb
|
106
112
|
- lib/rack/ssl-enforcer.rb
|
107
113
|
- lib/rack-ssl-enforcer.rb
|
108
114
|
- LICENSE
|
109
|
-
- README.
|
110
|
-
has_rdoc: true
|
115
|
+
- README.md
|
111
116
|
homepage: http://github.com/tobmatth/rack-ssl-enforcer
|
112
117
|
licenses: []
|
113
118
|
|
@@ -121,6 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
121
126
|
requirements:
|
122
127
|
- - ">="
|
123
128
|
- !ruby/object:Gem::Version
|
129
|
+
hash: 3
|
124
130
|
segments:
|
125
131
|
- 0
|
126
132
|
version: "0"
|
@@ -129,6 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
135
|
requirements:
|
130
136
|
- - ">="
|
131
137
|
- !ruby/object:Gem::Version
|
138
|
+
hash: 23
|
132
139
|
segments:
|
133
140
|
- 1
|
134
141
|
- 3
|
@@ -137,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
144
|
requirements: []
|
138
145
|
|
139
146
|
rubyforge_project: rack-ssl-enforcer
|
140
|
-
rubygems_version: 1.
|
147
|
+
rubygems_version: 1.8.24
|
141
148
|
signing_key:
|
142
149
|
specification_version: 3
|
143
150
|
summary: A simple Rack middleware to enforce SSL
|
data/README.rdoc
DELETED
@@ -1,111 +0,0 @@
|
|
1
|
-
= Rack::SslEnforcer
|
2
|
-
|
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 by default (HSTS must be set manually).
|
5
|
-
|
6
|
-
Tested on Ruby 1.8.7, 1.9.2, REE, JRuby and Rubinius.
|
7
|
-
|
8
|
-
== Installation
|
9
|
-
|
10
|
-
gem install rack-ssl-enforcer
|
11
|
-
|
12
|
-
|
13
|
-
== Basic Usage
|
14
|
-
|
15
|
-
require 'rack/ssl-enforcer'
|
16
|
-
use Rack::SslEnforcer
|
17
|
-
|
18
|
-
Or, if you are using Bundler, just add this to your Gemfile:
|
19
|
-
|
20
|
-
gem 'rack-ssl-enforcer'
|
21
|
-
|
22
|
-
To use Rack::SslEnforcer in your Rails application, add the following line to your application
|
23
|
-
config file (<tt>config/application.rb</tt> for Rails 3, <tt>config/environment.rb</tt> for Rails 2):
|
24
|
-
|
25
|
-
config.middleware.use Rack::SslEnforcer
|
26
|
-
|
27
|
-
If all you want is SSL for your whole application, you are done! However, you can specify some
|
28
|
-
|
29
|
-
|
30
|
-
== Options
|
31
|
-
|
32
|
-
You might need the :redirect_to option if the requested URL can't be determined (e.g. if using a proxy).
|
33
|
-
|
34
|
-
config.middleware.use Rack::SslEnforcer, :redirect_to => 'https://example.org'
|
35
|
-
|
36
|
-
You can also define specific regex patterns or paths or hosts to redirect.
|
37
|
-
|
38
|
-
config.middleware.use Rack::SslEnforcer, :only => /^\/admin\//
|
39
|
-
config.middleware.use Rack::SslEnforcer, :only => "/login"
|
40
|
-
config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/]
|
41
|
-
config.middleware.use Rack::SslEnforcer, :only_hosts => 'api.example.com'
|
42
|
-
config.middleware.use Rack::SslEnforcer, :only_hosts => [/[www|api]\.example\.org$/, 'example.com']
|
43
|
-
config.middleware.use Rack::SslEnforcer, :except_hosts => 'help.example.com'
|
44
|
-
config.middleware.use Rack::SslEnforcer, :except_hosts => /[help|blog]\.example\.com$/
|
45
|
-
|
46
|
-
Note: hosts options take precedence over the path options. See tests for examples.
|
47
|
-
|
48
|
-
Use the :strict option to force http for all requests not matching your :only specification
|
49
|
-
|
50
|
-
config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/], :strict => true
|
51
|
-
config.middleware.use Rack::SslEnforcer, :only_hosts => 'api.example.com', :strict => true
|
52
|
-
|
53
|
-
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.
|
54
|
-
|
55
|
-
config.middleware.use Rack::SslEnforcer, :only => [%r{^/users/}], :mixed => true
|
56
|
-
|
57
|
-
The above will allow you to post/put from the secure/non-secure urls keeping the original schema.
|
58
|
-
|
59
|
-
To set HSTS expiry and subdomain inclusion (defaults: one year, true). Strict option disables HSTS.
|
60
|
-
|
61
|
-
config.middleware.use Rack::SslEnforcer, :hsts => { :expires => 500, :subdomains => false }
|
62
|
-
config.middleware.use Rack::SslEnforcer, :hsts => true # equivalent to { :expires => 31536000, :subdomains => true }
|
63
|
-
|
64
|
-
Finally you might want to share a cookie based session between http and https.
|
65
|
-
This is not possible by default with Rack::SslEnforcer for security reasons.
|
66
|
-
See: [http://en.wikipedia.org/wiki/HTTP_cookie#Cookie_theft_and_session_hijacking]
|
67
|
-
|
68
|
-
Nevertheless, you can set the option :force_secure_cookies to false in order to be able to share a cookie based session between http and https:
|
69
|
-
|
70
|
-
config.middleware.use Rack::SslEnforcer, :only => "/login", :force_secure_cookies => false
|
71
|
-
|
72
|
-
But be aware that if you do so, you have to make sure that the content of you cookie is encoded.
|
73
|
-
This can be done using a coder with Rack::Session::Cookie.
|
74
|
-
See: [https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb#L28-42]
|
75
|
-
|
76
|
-
|
77
|
-
== TODO
|
78
|
-
|
79
|
-
* Add configuration option to specify local http / https ports
|
80
|
-
* Cleanup tests
|
81
|
-
|
82
|
-
|
83
|
-
== Contributors
|
84
|
-
|
85
|
-
* {Dan Mayer}[http://github.com/danmayer]
|
86
|
-
* {Rémy Coutable}[http://github.com/rymai]
|
87
|
-
* {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
|
88
|
-
* {Paul Annesley}[https://github.com/pda]
|
89
|
-
* {Saimon Moore}[https://github.com/saimonmoore]
|
90
|
-
|
91
|
-
|
92
|
-
== Credits
|
93
|
-
|
94
|
-
Flagging cookies as secure functionality and HSTS support is greatly inspired by {Joshua Peek's Rack::SSL}[https://github.com/josh/rack-ssl]
|
95
|
-
|
96
|
-
|
97
|
-
== Note on Patches/Pull Requests
|
98
|
-
|
99
|
-
* Fork the project.
|
100
|
-
* Make your feature addition or bug fix.
|
101
|
-
* Add tests for it. This is important so I don't break it in a
|
102
|
-
future version unintentionally.
|
103
|
-
* Commit, do not mess with rakefile, version, or history.
|
104
|
-
(if you want to have your own version,
|
105
|
-
that is fine but bump version in a commit by itself I can ignore when I pull)
|
106
|
-
* Send me a pull request. Bonus points for topic branches.
|
107
|
-
|
108
|
-
|
109
|
-
== Copyright
|
110
|
-
|
111
|
-
Copyright (c) 2010 Tobias Matthies. See LICENSE for details.
|