Skip to content

Commit 3b83758

Browse files
searlstenderloveguilleiguaranvinibispo
committed
Enable force_ssl=true in production by default
I will admit to deploying an app into production and leaving it there for weeks before realizing that authenticated traffic was being transported un-secured HTTP. I'd been operating under the false assumption that `config.force_ssl` would be `true` in production by default for new apps. Suggesting this change to gauge interest and start a conversation. Since this option was introduced, the state of the web has really changed with Let's Encrypt certificates, and HTTPS has become table stakes for most hosting services. It feels like the time is right to enable Strict-Transport-Security by default for new apps. Co-authored-by: Aaron Patterson <[email protected]> Co-authored-by: Guillermo Iguaran <[email protected]> Co-authored-by: vinibispo <[email protected]>
1 parent 3fc95e1 commit 3b83758

File tree

11 files changed

+94
-80
lines changed

11 files changed

+94
-80
lines changed

railties/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Enable force_ssl=true in production by default: Force all access to the app over SSL,
2+
use Strict-Transport-Security, and use secure cookies
3+
4+
*Justin Searls*, *Aaron Patterson*, *Guillermo Iguaran*, *Vinícius Bispo*
5+
16
* Add engine's draw paths to application route set, so that the application
27
can draw route files defined in engine paths.
38

railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Rails.application.configure do
5757
# config.assume_ssl = true
5858

5959
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
60-
# config.force_ssl = true
60+
config.force_ssl = true
6161

6262
# Log to STDOUT by default
6363
config.logger = ActiveSupport::Logger.new(STDOUT)

railties/test/application/asset_debugging_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def teardown
4949
class ::PostsController < ActionController::Base ; end
5050

5151
# the debug_assets params isn't used if compile is off
52-
get "/posts?debug_assets=true"
52+
get("/posts?debug_assets=true", {}, "HTTPS" => "on")
5353
assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body)
5454
assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
5555
end
@@ -62,7 +62,7 @@ class ::PostsController < ActionController::Base ; end
6262

6363
class ::PostsController < ActionController::Base ; end
6464

65-
get "/posts?debug_assets=true"
65+
get("/posts?debug_assets=true", {}, "HTTPS" => "on")
6666
assert_match(/<script src="\/assets\/application(\.debug|\.self)?-([0-z]+)\.js(\?body=1)?"><\/script>/, last_response.body)
6767
end
6868

railties/test/application/assets_test.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ class User < ActiveRecord::Base; raise 'should not be reached'; end
263263

264264
# Checking if Uglifier is defined we can know if Sprockets was reached or not
265265
assert_not defined?(Uglifier)
266-
get "/assets/#{asset_path}"
266+
get("/assets/#{asset_path}", {}, "HTTPS" => "on")
267267
assert_match "alert()", last_response.body
268268
assert_not defined?(Uglifier)
269269
end
@@ -348,7 +348,7 @@ class User < ActiveRecord::Base; raise 'should not be reached'; end
348348
# Load app env
349349
app "production"
350350

351-
get "/assets/demo.js"
351+
get("/assets/demo.js", {}, "HTTPS" => "on")
352352
assert_equal 404, last_response.status
353353
end
354354

@@ -410,7 +410,7 @@ def index
410410
class ::PostsController < ActionController::Base ; end
411411

412412
# the debug_assets params isn't used if compile is off
413-
get "/posts?debug_assets=true"
413+
get("/posts?debug_assets=true", {}, "HTTPS" => "on")
414414
assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body)
415415
assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
416416
end

railties/test/application/loading_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ def show
446446
require "rack/test"
447447
extend Rack::Test::Methods
448448

449-
get "/omg/show"
449+
get("/omg/show", {}, "HTTPS" => "on")
450450
assert_equal "Query cache is enabled.", last_response.body
451451
end
452452

railties/test/application/mailer_previews_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ def teardown
2626

2727
test "/rails/mailers is not accessible in production" do
2828
app("production")
29-
get "/rails/mailers"
29+
get("/rails/mailers", {}, { "HTTPS" => "on" })
3030
assert_equal 404, last_response.status
3131
end
3232

3333
test "/rails/mailers is accessible with correct configuration" do
3434
add_to_config "config.action_mailer.show_previews = true"
3535
app("production")
36-
get "/rails/mailers", {}, { "REMOTE_ADDR" => "4.2.42.42" }
36+
get "/rails/mailers", {}, { "REMOTE_ADDR" => "4.2.42.42", "HTTPS" => "on" }
3737
assert_equal 200, last_response.status
3838
end
3939

railties/test/application/middleware/cache_test.rb

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ def setup
1010
build_app
1111
require "rack/test"
1212
extend Rack::Test::Methods
13+
14+
add_to_env_config "production", "config.cache_store = :memory_store"
15+
add_to_env_config "production", "config.action_dispatch.rack_cache = true"
1316
end
1417

1518
def teardown
@@ -56,7 +59,7 @@ def test_cache_keeps_if_modified_since
5659
simple_controller
5760
expected = "Wed, 30 May 1984 19:43:31 GMT"
5861

59-
get "/expires/keeps_if_modified_since", {}, { "HTTP_IF_MODIFIED_SINCE" => expected }
62+
get "/expires/keeps_if_modified_since", {}, { "HTTP_IF_MODIFIED_SINCE" => expected, "HTTPS" => "on" }
6063

6164
assert_equal 200, last_response.status
6265
assert_equal expected, last_response.body, "cache should have kept If-Modified-Since"
@@ -79,15 +82,13 @@ def test_cache_is_disabled_in_dev_mode
7982
def test_cache_works_with_expires
8083
simple_controller
8184

82-
add_to_config "config.action_dispatch.rack_cache = true"
83-
84-
get "/expires/expires_header"
85+
get "/expires/expires_header", {}, { "HTTPS" => "on" }
8586
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
8687
assert_equal "max-age=10, public", last_response.headers["Cache-Control"]
8788

8889
body = last_response.body
8990

90-
get "/expires/expires_header"
91+
get "/expires/expires_header", {}, { "HTTPS" => "on" }
9192

9293
assert_equal "fresh", last_response.headers["X-Rack-Cache"]
9394

@@ -97,31 +98,27 @@ def test_cache_works_with_expires
9798
def test_cache_works_with_expires_private
9899
simple_controller
99100

100-
add_to_config "config.action_dispatch.rack_cache = true"
101-
102-
get "/expires/expires_header", private: true
101+
get "/expires/expires_header", { private: true }, { "HTTPS" => "on" }
103102
assert_equal "miss", last_response.headers["X-Rack-Cache"]
104103
assert_equal "private, max-age=10", last_response.headers["Cache-Control"]
105104

106105
body = last_response.body
107106

108-
get "/expires/expires_header", private: true
107+
get "/expires/expires_header", { private: true }, { "HTTPS" => "on" }
109108
assert_equal "miss", last_response.headers["X-Rack-Cache"]
110109
assert_not_equal body, last_response.body
111110
end
112111

113112
def test_cache_works_with_etags
114113
simple_controller
115114

116-
add_to_config "config.action_dispatch.rack_cache = true"
117-
118-
get "/expires/expires_etag"
119-
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
115+
get "/expires/expires_etag", {}, { "HTTPS" => "on" }
120116
assert_equal "public", last_response.headers["Cache-Control"]
117+
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
121118

122119
etag = last_response.headers["ETag"]
123120

124-
get "/expires/expires_etag", {}, { "HTTP_IF_NONE_MATCH" => etag }
121+
get "/expires/expires_etag", {}, { "HTTP_IF_NONE_MATCH" => etag, "HTTPS" => "on" }
125122
assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
126123
assert_equal 304, last_response.status
127124
assert_equal "", last_response.body
@@ -130,32 +127,28 @@ def test_cache_works_with_etags
130127
def test_cache_works_with_etags_private
131128
simple_controller
132129

133-
add_to_config "config.action_dispatch.rack_cache = true"
134-
135-
get "/expires/expires_etag", private: true
130+
get "/expires/expires_etag", { private: true }, { "HTTPS" => "on" }
136131
assert_equal "miss", last_response.headers["X-Rack-Cache"]
137132
assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
138133

139134
body = last_response.body
140135
etag = last_response.headers["ETag"]
141136

142-
get "/expires/expires_etag", { private: true }, { "HTTP_IF_NONE_MATCH" => etag }
137+
get "/expires/expires_etag", { private: true }, { "HTTP_IF_NONE_MATCH" => etag, "HTTPS" => "on" }
143138
assert_equal "miss", last_response.headers["X-Rack-Cache"]
144139
assert_not_equal body, last_response.body
145140
end
146141

147142
def test_cache_works_with_last_modified
148143
simple_controller
149144

150-
add_to_config "config.action_dispatch.rack_cache = true"
151-
152-
get "/expires/expires_last_modified"
153-
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
145+
get "/expires/expires_last_modified", {}, { "HTTPS" => "on" }
154146
assert_equal "public", last_response.headers["Cache-Control"]
147+
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
155148

156149
last = last_response.headers["Last-Modified"]
157150

158-
get "/expires/expires_last_modified", {}, { "HTTP_IF_MODIFIED_SINCE" => last }
151+
get "/expires/expires_last_modified", {}, { "HTTP_IF_MODIFIED_SINCE" => last, "HTTPS" => "on" }
159152
assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
160153
assert_equal 304, last_response.status
161154
assert_equal "", last_response.body
@@ -164,16 +157,14 @@ def test_cache_works_with_last_modified
164157
def test_cache_works_with_last_modified_private
165158
simple_controller
166159

167-
add_to_config "config.action_dispatch.rack_cache = true"
168-
169-
get "/expires/expires_last_modified", private: true
160+
get "/expires/expires_last_modified", { private: true }, { "HTTPS" => "on" }
170161
assert_equal "miss", last_response.headers["X-Rack-Cache"]
171162
assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
172163

173164
body = last_response.body
174165
last = last_response.headers["Last-Modified"]
175166

176-
get "/expires/expires_last_modified", { private: true }, { "HTTP_IF_MODIFIED_SINCE" => last }
167+
get "/expires/expires_last_modified", { private: true }, { "HTTP_IF_MODIFIED_SINCE" => last, "HTTPS" => "on" }
177168
assert_equal "miss", last_response.headers["X-Rack-Cache"]
178169
assert_not_equal body, last_response.body
179170
end

railties/test/application/middleware/exceptions_test.rb

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def index
2626
RUBY
2727

2828
log = capture(:stdout) do
29-
get "/foo"
29+
get("/foo", {}, "HTTPS" => "on")
3030
assert_equal 500, last_response.status
3131
end
3232

@@ -43,12 +43,12 @@ def index
4343
end
4444
RUBY
4545

46-
get "/foo"
46+
get "/foo", {}, { "HTTPS" => "on" }
4747
assert_equal 404, last_response.status
4848
end
4949

5050
test "renders unknown http methods as 405" do
51-
request "/", { "REQUEST_METHOD" => "NOT_AN_HTTP_METHOD" }
51+
request("/", { "REQUEST_METHOD" => "NOT_AN_HTTP_METHOD", "HTTPS" => "on" })
5252
assert_equal 405, last_response.status
5353
end
5454

@@ -62,7 +62,7 @@ def index
6262

6363
app.config.action_dispatch.show_exceptions = :all
6464

65-
request "/", { "REQUEST_METHOD" => "NOT_AN_HTTP_METHOD" }
65+
request "/", { "REQUEST_METHOD" => "NOT_AN_HTTP_METHOD", "HTTPS" => "on" }
6666
assert_equal 405, last_response.status
6767
end
6868

@@ -90,7 +90,7 @@ def not_acceptable
9090
add_to_config "config.action_dispatch.show_exceptions = :all"
9191
add_to_config "config.consider_all_requests_local = false"
9292

93-
get "/foo", {}, { "HTTP_ACCEPT" => "invalid" }
93+
get "/foo", {}, { "HTTP_ACCEPT" => "invalid", "HTTPS" => "on" }
9494
assert_equal 406, last_response.status
9595
assert_not_equal "rendering index!", last_response.body
9696
end
@@ -104,7 +104,7 @@ def not_acceptable
104104

105105
app.config.action_dispatch.show_exceptions = :all
106106

107-
get "/foo"
107+
get("/foo", {}, "HTTPS" => "on")
108108
assert_equal 404, last_response.status
109109
assert_equal "YOU FAILED", last_response.body
110110
end
@@ -120,23 +120,23 @@ def index
120120

121121
app.config.action_dispatch.show_exceptions = :all
122122

123-
get "/foo"
123+
get("/foo", {}, "HTTPS" => "on")
124124
assert_equal 500, last_response.status
125125
end
126126

127127
test "unspecified route when action_dispatch.show_exceptions is not set raises an exception" do
128128
app.config.action_dispatch.show_exceptions = :none
129129

130130
assert_raise(ActionController::RoutingError) do
131-
get "/foo"
131+
get("/foo", {}, "HTTPS" => "on")
132132
end
133133
end
134134

135135
test "unspecified route when action_dispatch.show_exceptions is set shows 404" do
136136
app.config.action_dispatch.show_exceptions = :all
137137

138138
assert_nothing_raised do
139-
get "/foo"
139+
get("/foo", {}, "HTTPS" => "on")
140140
assert_match "The page you were looking for doesn't exist.", last_response.body
141141
end
142142
end
@@ -146,7 +146,7 @@ def index
146146
app.config.consider_all_requests_local = true
147147

148148
assert_nothing_raised do
149-
get "/foo"
149+
get("/foo", {}, "HTTPS" => "on")
150150
assert_match "No route matches", last_response.body
151151
end
152152
end
@@ -161,7 +161,7 @@ def index
161161
app.config.action_dispatch.show_exceptions = :all
162162
app.config.consider_all_requests_local = true
163163

164-
get "/articles"
164+
get("/articles", {}, "HTTPS" => "on")
165165
assert_match "<title>Action Controller: Exception caught</title>", last_response.body
166166
end
167167

@@ -181,7 +181,7 @@ def index
181181
✓測試テスト시험
182182
ERB
183183

184-
get "/foo", utf8: "✓"
184+
get("/foo", { utf8: "✓" }, { "HTTPS" => "on" })
185185
assert_match(/boooom/, last_response.body)
186186
assert_match(/測試テスト시험/, last_response.body)
187187
end
@@ -197,7 +197,7 @@ def index
197197
app.config.action_dispatch.show_exceptions = :all
198198
app.config.consider_all_requests_local = true
199199

200-
get "/foo?x[y]=1&x[y][][w]=2"
200+
get "/foo?x[y]=1&x[y][][w]=2", {}, "HTTPS" => "on"
201201
assert_equal 400, last_response.status
202202
assert_match "Invalid query parameters", last_response.body
203203
end
@@ -216,7 +216,7 @@ def index
216216
limit = Rack::Utils.param_depth_limit + 1
217217
malicious_url = "/foo?#{'[test]' * limit}=test"
218218

219-
get malicious_url
219+
get(malicious_url, {}, "HTTPS" => "on")
220220
assert_equal 400, last_response.status
221221
assert_match "Invalid query parameters", last_response.body
222222
end
@@ -233,12 +233,12 @@ def index
233233
app.config.consider_all_requests_local = true
234234
app.config.action_dispatch.ignore_accept_header = false
235235

236-
get "/foo"
236+
get("/foo", {}, "HTTPS" => "on")
237237
assert_equal 500, last_response.status
238238
assert_match "<title>Action Controller: Exception caught</title>", last_response.body
239239
assert_match "ActiveRecord::StatementInvalid", last_response.body
240240

241-
get "/foo", {}, { "HTTP_ACCEPT" => "text/plain", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" }
241+
get "/foo", {}, { "HTTP_ACCEPT" => "text/plain", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", "HTTPS" => "on" }
242242
assert_equal 500, last_response.status
243243
assert_equal "text/plain", last_response.media_type
244244
assert_match "ActiveRecord::StatementInvalid", last_response.body
@@ -255,7 +255,7 @@ def index
255255

256256
app.config.action_dispatch.show_exceptions = :rescuable
257257

258-
get "/foo"
258+
get "/foo", {}, { "HTTPS" => "on" }
259259
assert_equal 404, last_response.status
260260
end
261261

@@ -270,7 +270,7 @@ def index
270270

271271
app.config.action_dispatch.show_exceptions = :rescuable
272272

273-
error = assert_raises(RuntimeError) { get "/foo" }
273+
error = assert_raises(RuntimeError) { get("/foo", {}, { "HTTPS" => "on" }) }
274274
assert_equal "oops", error.message
275275
end
276276
end

0 commit comments

Comments
 (0)