aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb13
-rw-r--r--actionpack/test/dispatch/ssl_test.rb23
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb4
-rw-r--r--railties/test/generators/app_generator_test.rb28
6 files changed, 69 insertions, 8 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index 735b5939dd..d6a0fe650c 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -23,7 +23,7 @@ module ActionDispatch
# preload lists is `18.weeks`.
# * `subdomains`: Set to `true` to tell the browser to apply these settings
# to all subdomains. This protects your cookies from interception by a
- # vulnerable site on a subdomain. Defaults to `false`.
+ # vulnerable site on a subdomain. Defaults to `true`.
# * `preload`: Advertise that this site may be included in browsers'
# preloaded HSTS lists. HSTS protects your site on every visit *except the
# first visit* since it hasn't seen your HSTS header yet. To close this
@@ -57,6 +57,17 @@ module ActionDispatch
end
@secure_cookies = secure_cookies
+
+ if hsts != true && hsts != false && hsts[:subdomains].nil?
+ hsts[:subdomains] = false
+
+ ActiveSupport::Deprecation.warn <<-end_warning.strip_heredoc
+ In Rails 5.1, The `:subdomains` option of HSTS config will be treated as true if
+ unspecified. Set `config.ssl_options = { hsts: { subdomains: false }}` to opt out
+ of this behavior.
+ end_warning
+ end
+
@hsts_header = build_hsts_header(normalize_hsts_options(hsts))
end
diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb
index c66a0e6a7a..18ff894b31 100644
--- a/actionpack/test/dispatch/ssl_test.rb
+++ b/actionpack/test/dispatch/ssl_test.rb
@@ -7,7 +7,7 @@ class SSLTest < ActionDispatch::IntegrationTest
def build_app(headers: {}, ssl_options: {})
headers = HEADERS.merge(headers)
- ActionDispatch::SSL.new lambda { |env| [200, headers, []] }, ssl_options
+ ActionDispatch::SSL.new lambda { |env| [200, headers, []] }, ssl_options.reverse_merge(hsts: { subdomains: true })
end
end
@@ -98,15 +98,16 @@ end
class StrictTransportSecurityTest < SSLTest
EXPECTED = 'max-age=15552000'
+ EXPECTED_WITH_SUBDOMAINS = 'max-age=15552000; includeSubDomains'
- def assert_hsts(expected, url: 'https://example.org', hsts: {}, headers: {})
+ def assert_hsts(expected, url: 'https://example.org', hsts: { subdomains: true }, headers: {})
self.app = build_app ssl_options: { hsts: hsts }, headers: headers
get url
assert_equal expected, response.headers['Strict-Transport-Security']
end
test 'enabled by default' do
- assert_hsts EXPECTED
+ assert_hsts EXPECTED_WITH_SUBDOMAINS
end
test 'not sent with http:// responses' do
@@ -126,11 +127,15 @@ class StrictTransportSecurityTest < SSLTest
end
test ':expires sets max-age' do
- assert_hsts 'max-age=500', hsts: { expires: 500 }
+ assert_deprecated do
+ assert_hsts 'max-age=500', hsts: { expires: 500 }
+ end
end
test ':expires supports AS::Duration arguments' do
- assert_hsts 'max-age=31557600', hsts: { expires: 1.year }
+ assert_deprecated do
+ assert_hsts 'max-age=31557600', hsts: { expires: 1.year }
+ end
end
test 'include subdomains' do
@@ -142,11 +147,15 @@ class StrictTransportSecurityTest < SSLTest
end
test 'opt in to browser preload lists' do
- assert_hsts "#{EXPECTED}; preload", hsts: { preload: true }
+ assert_deprecated do
+ assert_hsts "#{EXPECTED}; preload", hsts: { preload: true }
+ end
end
test 'opt out of browser preload lists' do
- assert_hsts EXPECTED, hsts: { preload: false }
+ assert_deprecated do
+ assert_hsts EXPECTED, hsts: { preload: false }
+ end
end
end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 40140c49f6..5a4ad2ff99 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Enable HSTS with IncludeSudomains header for new applications.
+
+ *Egor Homakov*, *Prathamesh Sonpatki*
+
## Rails 5.0.0.beta3 (February 24, 2016) ##
* Alias `rake` with `rails_command` in the Rails Application Templates API
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 07d38605a2..4234706452 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -92,6 +92,7 @@ module Rails
callback_terminator_config_exist = File.exist?('config/initializers/callback_terminator.rb')
active_record_belongs_to_required_by_default_config_exist = File.exist?('config/initializers/active_record_belongs_to_required_by_default.rb')
action_cable_config_exist = File.exist?('config/cable.yml')
+ ssl_options_exist = File.exist?('config/initializers/ssl_options.rb')
config
@@ -110,6 +111,10 @@ module Rails
unless action_cable_config_exist
template 'config/cable.yml'
end
+
+ unless ssl_options_exist
+ remove_file 'config/initializers/ssl_options.rb'
+ end
end
def database_yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb
new file mode 100644
index 0000000000..1775dea1e7
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure SSL options to enable HSTS with subdomains.
+Rails.application.config.ssl_options = { hsts: { subdomains: true } }
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index bc80c7eb1b..ec8ec4787f 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -241,6 +241,34 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_rails_update_does_not_create_ssl_options_by_default
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ FileUtils.rm("#{app_root}/config/initializers/ssl_options.rb")
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_no_file "#{app_root}/config/initializers/ssl_options.rb"
+ end
+ end
+
+ def test_rails_update_does_not_remove_ssl_options_if_already_present
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ FileUtils.touch("#{app_root}/config/initializers/ssl_options.rb")
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_file "#{app_root}/config/initializers/ssl_options.rb"
+ end
+ end
+
def test_application_names_are_not_singularized
run_generator [File.join(destination_root, "hats")]
assert_file "hats/config/environment.rb", /Rails\.application\.initialize!/