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.
- data/README.rdoc +47 -20
- data/lib/rack-ssl-enforcer.rb +1 -0
- data/lib/rack/ssl-enforcer.rb +80 -25
- data/lib/rack/ssl-enforcer/version.rb +2 -2
- metadata +34 -37
- data/lib/rack/rack-ssl-enforcer.rb +0 -1
data/README.rdoc
CHANGED
@@ -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
|
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
|
14
|
+
require 'rack/ssl-enforcer'
|
15
15
|
use Rack::SslEnforcer
|
16
16
|
|
17
|
-
|
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 =>
|
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
|
-
|
45
|
+
Note: hosts options take precedence over the path options. See tests for examples.
|
34
46
|
|
35
|
-
|
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'
|
data/lib/rack/ssl-enforcer.rb
CHANGED
@@ -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?(
|
10
|
+
if enforce_ssl?(@req)
|
11
11
|
scheme = 'https' unless ssl_request?(env)
|
12
|
-
elsif ssl_request?(env) &&
|
12
|
+
elsif ssl_request?(env) && enforcement_non_ssl?(env)
|
13
13
|
scheme = 'http'
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
if scheme
|
17
|
-
location =
|
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
|
49
|
-
if
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
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
|
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: 21
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
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:
|
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
|
-
-
|
32
|
-
-
|
33
|
-
version: 0.
|
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
|
-
-
|
48
|
-
version: 0.1.
|
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: &
|
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
|
-
-
|
76
|
-
version: 1.0.
|
66
|
+
- 10
|
67
|
+
version: 1.0.10
|
77
68
|
type: :development
|
78
|
-
version_requirements: *
|
69
|
+
version_requirements: *id003
|
79
70
|
- !ruby/object:Gem::Dependency
|
80
71
|
name: test-unit
|
81
72
|
prerelease: false
|
82
|
-
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: *
|
85
|
+
version_requirements: *id004
|
94
86
|
- !ruby/object:Gem::Dependency
|
95
87
|
name: shoulda
|
96
88
|
prerelease: false
|
97
|
-
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: *
|
101
|
+
version_requirements: *id005
|
109
102
|
- !ruby/object:Gem::Dependency
|
110
103
|
name: rack
|
111
104
|
prerelease: false
|
112
|
-
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: *
|
117
|
+
version_requirements: *id006
|
124
118
|
- !ruby/object:Gem::Dependency
|
125
119
|
name: rack-test
|
126
120
|
prerelease: false
|
127
|
-
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: *
|
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.
|
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'
|