honkster-rack-ssl-enforcer 0.2.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Tobias Matthies
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,111 @@
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.
@@ -0,0 +1 @@
1
+ require 'rack/ssl-enforcer'
@@ -0,0 +1,174 @@
1
+ module Rack
2
+ class SslEnforcer
3
+
4
+ # Warning: If you set the option force_secure_cookies to false, make sure that your cookies
5
+ # are encoded and that you understand the consequences (see documentation)
6
+ def initialize(app, options={})
7
+ default_options = {
8
+ :redirect_to => nil,
9
+ :only => nil,
10
+ :only_hosts => nil,
11
+ :except => nil,
12
+ :except_hosts => nil,
13
+ :strict => false,
14
+ :mixed => false,
15
+ :hsts => nil,
16
+ :http_port => nil,
17
+ :https_port => nil,
18
+ :force_secure_cookies => true
19
+ }
20
+ @app, @options = app, default_options.merge(options)
21
+ end
22
+
23
+ def call(env)
24
+ @req = Rack::Request.new(env)
25
+ if enforce_ssl?(@req)
26
+ scheme = 'https' unless ssl_request?(env)
27
+ elsif ssl_request?(env) && enforcement_non_ssl?(env)
28
+ scheme = 'http'
29
+ end
30
+
31
+ if scheme
32
+ location = replace_scheme(@req, scheme).url
33
+ body = "<html><body>You are being <a href=\"#{location}\">redirected</a>.</body></html>"
34
+ [301, { 'Content-Type' => 'text/html', 'Location' => location }, [body]]
35
+ elsif ssl_request?(env)
36
+ status, headers, body = @app.call(env)
37
+ flag_cookies_as_secure!(headers) if @options[:force_secure_cookies]
38
+ set_hsts_headers!(headers) if @options[:hsts] && !@options[:strict]
39
+ [status, headers, body]
40
+ else
41
+ @app.call(env)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def enforcement_non_ssl?(env)
48
+ true if @options[:strict] || @options[:mixed] && !(env['REQUEST_METHOD'] == 'PUT' || env['REQUEST_METHOD'] == 'POST')
49
+ end
50
+
51
+ def ssl_request?(env)
52
+ scheme(env) == 'https'
53
+ end
54
+
55
+ # Fixed in rack >= 1.3
56
+ def scheme(env)
57
+ if env['HTTPS'] == 'on'
58
+ 'https'
59
+ elsif env['HTTP_X_FORWARDED_PROTO']
60
+ env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
61
+ else
62
+ env['rack.url_scheme']
63
+ end
64
+ end
65
+
66
+ def matches?(key, pattern, req)
67
+ if pattern.is_a?(Regexp)
68
+ case key
69
+ when :only
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
78
+ else
79
+ case key
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|
95
+ rules = [@options[key]].flatten.compact
96
+ unless rules.empty?
97
+ rules.send(key == :except_hosts || key == :except ? "all?" : "any?") do |pattern|
98
+ matches?(key, pattern, req)
99
+ end
100
+ end
101
+ end
102
+ else
103
+ false
104
+ end
105
+ end
106
+
107
+ def enforce_ssl?(req)
108
+ path_keys = [:only, :except]
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
125
+ end
126
+
127
+ def replace_scheme(req, scheme)
128
+ if @options[:redirect_to]
129
+ uri = URI.split(@options[:redirect_to])
130
+ uri = uri[2] || uri[5]
131
+ else
132
+ uri = nil
133
+ end
134
+ Rack::Request.new(req.env.merge(
135
+ 'rack.url_scheme' => scheme,
136
+ 'HTTP_X_FORWARDED_PROTO' => scheme,
137
+ 'HTTP_X_FORWARDED_PORT' => port_for(scheme).to_s,
138
+ 'SERVER_PORT' => port_for(scheme).to_s
139
+ ).merge(uri ? {'HTTP_HOST' => uri} : {}))
140
+ end
141
+
142
+ def port_for(scheme)
143
+ if scheme == 'https'
144
+ @options[:https_port] || 443
145
+ else
146
+ @options[:http_port] || 80
147
+ end
148
+ end
149
+
150
+ # see http://en.wikipedia.org/wiki/HTTP_cookie#Cookie_theft_and_session_hijacking
151
+ def flag_cookies_as_secure!(headers)
152
+ if cookies = headers['Set-Cookie']
153
+ # Support Rails 2.3 / Rack 1.1 arrays as headers
154
+ unless cookies.is_a?(Array)
155
+ cookies = cookies.split("\n")
156
+ end
157
+
158
+ headers['Set-Cookie'] = cookies.map do |cookie|
159
+ cookie !~ / secure;/ ? "#{cookie}; secure" : cookie
160
+ end.join("\n")
161
+ end
162
+ end
163
+
164
+ # see http://en.wikipedia.org/wiki/Strict_Transport_Security
165
+ def set_hsts_headers!(headers)
166
+ opts = { :expires => 31536000, :subdomains => true }
167
+ opts.merge!(@options[:hsts])
168
+ value = "max-age=#{opts[:expires]}"
169
+ value += "; includeSubDomains" if opts[:subdomains]
170
+ headers.merge!({ 'Strict-Transport-Security' => value })
171
+ end
172
+
173
+ end
174
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class SslEnforcer
3
+ VERSION = "0.2.2.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: honkster-rack-ssl-enforcer
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.2.2.1
6
+ platform: ruby
7
+ authors:
8
+ - Tobias Matthies
9
+ - Thibaud Guillaume-Gentil
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-07-27 00:00:00 -07:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: bundler
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ~>
24
+ - !ruby/object:Gem::Version
25
+ version: "1.0"
26
+ type: :development
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: guard
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: 0.3.3
37
+ type: :development
38
+ version_requirements: *id002
39
+ - !ruby/object:Gem::Dependency
40
+ name: guard-test
41
+ prerelease: false
42
+ requirement: &id003 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: "0.2"
48
+ type: :development
49
+ version_requirements: *id003
50
+ - !ruby/object:Gem::Dependency
51
+ name: test-unit
52
+ prerelease: false
53
+ requirement: &id004 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ version: "2.2"
59
+ type: :development
60
+ version_requirements: *id004
61
+ - !ruby/object:Gem::Dependency
62
+ name: shoulda
63
+ prerelease: false
64
+ requirement: &id005 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.11.3
70
+ type: :development
71
+ version_requirements: *id005
72
+ - !ruby/object:Gem::Dependency
73
+ name: rack
74
+ prerelease: false
75
+ requirement: &id006 !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: 1.2.0
81
+ type: :development
82
+ version_requirements: *id006
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ prerelease: false
86
+ requirement: &id007 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ version: 0.5.4
92
+ type: :development
93
+ version_requirements: *id007
94
+ description: Rack::SslEnforcer is a simple Rack middleware to enforce ssl connections
95
+ email:
96
+ - tm@mit2m.de
97
+ - thibaud@thibaud.me
98
+ executables: []
99
+
100
+ extensions: []
101
+
102
+ extra_rdoc_files: []
103
+
104
+ files:
105
+ - lib/rack-ssl-enforcer.rb
106
+ - lib/rack/ssl-enforcer/version.rb
107
+ - lib/rack/ssl-enforcer.rb
108
+ - LICENSE
109
+ - README.rdoc
110
+ has_rdoc: true
111
+ homepage: http://github.com/tobmatth/rack-ssl-enforcer
112
+ licenses: []
113
+
114
+ post_install_message:
115
+ rdoc_options: []
116
+
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: "0"
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 1.3.6
131
+ requirements: []
132
+
133
+ rubyforge_project: rack-ssl-enforcer
134
+ rubygems_version: 1.5.2
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: A simple Rack middleware to enforce SSL
138
+ test_files: []
139
+