1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Change default HTTP status to 308 for ActionDispatch::SSL.

308 status code introduced in https://tools.ietf.org/html/rfc7538
preserves the request method unlike 301 status code which would convert
POST requests to GET.
This commit is contained in:
Guo Xiang Tan 2020-06-18 09:56:08 +08:00
parent 2f87589630
commit f1e53be508
No known key found for this signature in database
GPG key ID: FBD110179AAC1F20
10 changed files with 64 additions and 6 deletions

View file

@ -1,3 +1,7 @@
* Change default redirection status code for non-GET/HEAD requests to 308 Permanent Redirect for `ActionDispatch::SSL`.
*Alan Tan*, *Oz Ben-David*
* Fix `follow_redirect!` to follow redirection with same HTTP verb when following * Fix `follow_redirect!` to follow redirection with same HTTP verb when following
a 308 redirection. a 308 redirection.

View file

@ -56,7 +56,7 @@ module ActionDispatch
{ expires: HSTS_EXPIRES_IN, subdomains: true, preload: false } { expires: HSTS_EXPIRES_IN, subdomains: true, preload: false }
end end
def initialize(app, redirect: {}, hsts: {}, secure_cookies: true) def initialize(app, redirect: {}, hsts: {}, secure_cookies: true, ssl_default_redirect_status: nil)
@app = app @app = app
@redirect = redirect @redirect = redirect
@ -65,6 +65,7 @@ module ActionDispatch
@secure_cookies = secure_cookies @secure_cookies = secure_cookies
@hsts_header = build_hsts_header(normalize_hsts_options(hsts)) @hsts_header = build_hsts_header(normalize_hsts_options(hsts))
@ssl_default_redirect_status = ssl_default_redirect_status
end end
def call(env) def call(env)
@ -132,6 +133,8 @@ module ActionDispatch
def redirection_status(request) def redirection_status(request)
if request.get? || request.head? if request.get? || request.head?
301 # Issue a permanent redirect via a GET request. 301 # Issue a permanent redirect via a GET request.
elsif @ssl_default_redirect_status
@ssl_default_redirect_status
else else
307 # Issue a fresh request redirect to preserve the HTTP method. 307 # Issue a fresh request redirect to preserve the HTTP method.
end end

View file

@ -21,7 +21,7 @@ class RedirectSSLTest < SSLTest
end end
def assert_redirected(redirect: {}, from: "http://a/b?c=d", to: from.sub("http", "https")) def assert_redirected(redirect: {}, from: "http://a/b?c=d", to: from.sub("http", "https"))
redirect = { status: 301, body: [] }.merge(redirect) redirect = { body: [] }.merge(redirect)
self.app = build_app ssl_options: { redirect: redirect } self.app = build_app ssl_options: { redirect: redirect }
@ -64,8 +64,22 @@ class RedirectSSLTest < SSLTest
assert_post_redirected assert_post_redirected
end end
test "redirect with non-301 status" do test "redirect with custom status" do
assert_redirected redirect: { status: 307 } assert_redirected redirect: { status: 308 }
end
test "redirect with ssl_default_redirect_status" do
self.app = build_app(ssl_options: { ssl_default_redirect_status: 308 })
get "http://a/b?c=d"
assert_response 301
assert_redirected_to "https://a/b?c=d"
post "http://a/b?c=d"
assert_response 308
assert_redirected_to "https://a/b?c=d"
end end
test "redirect with custom body" do test "redirect with custom body" do

View file

@ -628,6 +628,11 @@ Defaults to `'signed cookie'`.
value of the `SameSite` attribute when setting cookies. Defaults to `nil`, value of the `SameSite` attribute when setting cookies. Defaults to `nil`,
which means the `SameSite` attribute is not added. which means the `SameSite` attribute is not added.
* `config.action_dispatch.ssl_default_redirect_status` configures the default
HTTP status code used when redirecting non-GET/HEAD requests from HTTP to HTTPS
in the `ActionDispatch::SSL` middleware. Defaults to `308` as defined in
https://tools.ietf.org/html/rfc7538.
* `ActionDispatch::Callbacks.before` takes a block of code to run before the request. * `ActionDispatch::Callbacks.before` takes a block of code to run before the request.
* `ActionDispatch::Callbacks.after` takes a block of code to run after the request. * `ActionDispatch::Callbacks.after` takes a block of code to run after the request.

View file

@ -176,6 +176,10 @@ This change is backwards compatible for the majority of applications, in which c
Technically, however, controllers could configure `helpers_path` to point to a directoy in `$LOAD_PATH` that was not in the autoload paths. That use case is no longer supported out of the box. If the helper module is not autoloadable, the application is responsible for loading it before calling `helper`. Technically, however, controllers could configure `helpers_path` to point to a directoy in `$LOAD_PATH` that was not in the autoload paths. That use case is no longer supported out of the box. If the helper module is not autoloadable, the application is responsible for loading it before calling `helper`.
### Redirection to HTTPS from HTTP will now use the 308 HTTP status code
The default HTTP status code used in `ActionDispatch::SSL` when redirecting non-GET/HEAD requests from HTTP to HTTPS has been changed to `308` as defined in https://tools.ietf.org/html/rfc7538.
Upgrading from Rails 5.2 to Rails 6.0 Upgrading from Rails 5.2 to Rails 6.0
------------------------------------- -------------------------------------

View file

@ -175,6 +175,7 @@ module Rails
if respond_to?(:action_dispatch) if respond_to?(:action_dispatch)
action_dispatch.cookies_same_site_protection = :lax action_dispatch.cookies_same_site_protection = :lax
action_dispatch.ssl_default_redirect_status = 308
end end
if respond_to?(:action_controller) if respond_to?(:action_controller)

View file

@ -16,7 +16,8 @@ module Rails
middleware.use ::ActionDispatch::HostAuthorization, config.hosts, config.action_dispatch.hosts_response_app middleware.use ::ActionDispatch::HostAuthorization, config.hosts, config.action_dispatch.hosts_response_app
if config.force_ssl if config.force_ssl
middleware.use ::ActionDispatch::SSL, **config.ssl_options middleware.use ::ActionDispatch::SSL, **config.ssl_options,
ssl_default_redirect_status: config.action_dispatch.ssl_default_redirect_status
end end
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header

View file

@ -34,3 +34,7 @@
# Specify whether `ActiveSupport::TimeZone.utc_to_local` returns a time with an # Specify whether `ActiveSupport::TimeZone.utc_to_local` returns a time with an
# UTC offset or a UTC time. # UTC offset or a UTC time.
# ActiveSupport.utc_to_local_returns_utc_offset_times = true # ActiveSupport.utc_to_local_returns_utc_offset_times = true
# Change default HTTP status code to `308` when redirecting non-GET/HEAD requets
# to HTTPS in `ActionDispatch::SSL` middleware.
# Rails.application.config.action_dispatch.ssl_default_redirect_status = 308

View file

@ -2347,6 +2347,28 @@ module ApplicationTests
assert_equal :lax, Rails.application.config.action_dispatch.cookies_same_site_protection assert_equal :lax, Rails.application.config.action_dispatch.cookies_same_site_protection
end end
test "Rails.application.config.action_dispatch.ssl_default_redirect_status is 308 in 6.1 defaults" do
remove_from_config '.*config\.load_defaults.*\n'
add_to_config 'config.load_defaults "6.1"'
app "production"
assert_equal 308, Rails.application.config.action_dispatch.ssl_default_redirect_status
end
test "Rails.application.config.action_dispatch.ssl_default_redirect_status can be configured in an initializer" do
remove_from_config '.*config\.load_defaults.*\n'
add_to_config 'config.load_defaults "6.0"'
app_file "config/initializers/new_framework_defaults_6_1.rb", <<-RUBY
Rails.application.config.action_dispatch.ssl_default_redirect_status = 308
RUBY
app "production"
assert_equal 308, Rails.application.config.action_dispatch.ssl_default_redirect_status
end
test "ActiveSupport.utc_to_local_returns_utc_offset_times is true in 6.1 defaults" do test "ActiveSupport.utc_to_local_returns_utc_offset_times is true in 6.1 defaults" do
remove_from_config '.*config\.load_defaults.*\n' remove_from_config '.*config\.load_defaults.*\n'
add_to_config 'config.load_defaults "6.1"' add_to_config 'config.load_defaults "6.1"'

View file

@ -145,7 +145,7 @@ module ApplicationTests
add_to_config "config.ssl_options = { redirect: { host: 'example.com' } }" add_to_config "config.ssl_options = { redirect: { host: 'example.com' } }"
boot! boot!
assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware[2].args assert_equal [{ redirect: { host: "example.com" }, ssl_default_redirect_status: 308 }], Rails.application.middleware[2].args
end end
test "removing Active Record omits its middleware" do test "removing Active Record omits its middleware" do