honkster-rack-ssl-enforcer 0.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+