Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate controller level force_ssl #32277

Merged
merged 1 commit into from Mar 30, 2018

Conversation

@derekprior
Copy link
Contributor

@derekprior derekprior commented Mar 17, 2018

Today there are two common ways for Rails developers to force their
applications to communicate over HTTPS:

  • config.force_ssl is a setting in environment configurations that
    enables the ActionDispatch::SSL middleware. With this middleware
    enabled, all HTTP communication to your application will be redirected
    to HTTPS. The middleware also takes care of other best practices by
    setting HSTS headers, upgrading all cookies to secure only, etc.
  • The force_ssl controller method redirects HTTP requests to certain
    controllers to HTTPS.

As a consultant, I've seen many applications with misconfigured HTTPS
setups due to developers adding force_ssl to ApplicationController
and not enabling config.force_ssl. With this configuration, many
application requests can be served over HTTP such as assets, requests
that hit mounted engines, etc. In addition, because cookies are not
upgraded to secure only in this configuration and HSTS headers are not
set, it's possible for cookies that are meant to be secure to be sent
over HTTP.

The confusion between these two methods of forcing HTTPS is compounded
by the fact that they share an identical name. This makes finding
documentation on the "right" method confusing.

HTTPS throughout is quickly becomming table stakes for all web sites.
Sites are expected to operate over HTTPS for all communication,
sensitive or otherwise. Let's encourage use of the broader-reaching
ActionDispatch::SSL middleware and elminate this source of user
confusion. If, for some reason, applications need to expose certain
endpoints over HTTP they can do so by properly configuring
config.ssl_options.

@rails-bot
Copy link

@rails-bot rails-bot commented Mar 17, 2018

r? @sgrif

(@rails-bot has picked a reviewer for you, use r? to override)

Loading

### Force SSL

The `force_ssl` method on controllers has been deprecated and will be removed in
Rails 6.0. you are encouraged to enable `config.force_ssl` to enforce HTTPS
Copy link
Member

@composerinteralia composerinteralia Mar 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capital 'Y' in 'You'

Unrelated: I enjoy your show 🚲 🔔 and I am looking forward to your talk at RailsConf.

Loading

Copy link
Member

@sikachu sikachu Mar 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another place here re: 6.0 -> 6.1.

Loading

Copy link
Member

@sikachu sikachu Mar 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing — Look like "You" in "you are encouraged ..." has to be capitalized.

Loading

Copy link
Member

@sikachu sikachu left a comment

I have a few comments. Would love to @fxn's or @pixeltrix thought about the documentation aspect of this though. 🙇

Loading

actionpack/lib/action_controller/metal/force_ssl.rb Outdated Show resolved Hide resolved
Loading
actionpack/lib/action_controller/metal/force_ssl.rb Outdated Show resolved Hide resolved
Loading
### Force SSL

The `force_ssl` method on controllers has been deprecated and will be removed in
Rails 6.0. you are encouraged to enable `config.force_ssl` to enforce HTTPS
Copy link
Member

@sikachu sikachu Mar 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another place here re: 6.0 -> 6.1.

Loading

@pixeltrix
Copy link
Member

@pixeltrix pixeltrix commented Mar 19, 2018

@derekprior not 100% convinced of the misconfiguration argument - it's not something I've personally seen in the past few years on a variety of Rails apps and there's been one occurrence on a high-profile website I support where adding config.force_ssl = true broke other parts of the site due to the domain wide nature of HSTS headers. I think there's a time when this will be appropriate but I'm unsure we want to remove the flexibility that doing it in the controller provides whilst sites are still transitioning to HTTPS.

However it's not a hill I willing to die on if other Rails maintainers feel differently.

Loading

@derekprior
Copy link
Contributor Author

@derekprior derekprior commented Mar 19, 2018

@pixeltrix I should have been more specific when I said "many apps". I've seen it 4 times that I remember in the last few years of projects (3~6/year). Most recently, I submitted a PR to a project to switch to config.force_ssl and was told of incompatibilities in one aspect of the application the last time someone tried this (similar to what you cite). I think the better response to those incompatibilities is proper configuration of config.ssl_options until such time the incompatibilities can be remedied.

That said, I certainly expected some debate of this PR. I can see your points as well. I just think the expectation is that applications work entirely over HTTPS these days and that Rails should optimize for that. I feel like I'd be less likely to object to the controller-level construct if it wasn't identically named to the middleware level configuration.

Loading

@pixeltrix
Copy link
Member

@pixeltrix pixeltrix commented Mar 19, 2018

I think the better response to those incompatibilities is proper configuration of config.ssl_options until such time the incompatibilities can be remedied.

The problem is that not everything is possible through config.force_ssl - we initially tried that in the scenario I cite and then had to drop back down to the controller level.

Maybe this is fine given the timeframe - it's not going to be removed until 6.1 which will likely be at least 18-24 months away.

Loading

@sgrif
Copy link
Contributor

@sgrif sgrif commented Mar 28, 2018

I'm 100% in favor of this change (other than the version changes), but I will defer to @pixeltrix on this.

r? @pixeltrix

Loading

@rails-bot rails-bot assigned pixeltrix and unassigned sgrif Mar 28, 2018
@rafaelfranca
Copy link
Member

@rafaelfranca rafaelfranca commented Mar 28, 2018

Giving the timing I'm fine with deprecating this feature in Rails 6.0 too.

@jeremy care to review this too given you did the last improvement on the SSL middleware?

Loading

jeremy
jeremy approved these changes Mar 28, 2018
Copy link
Member

@jeremy jeremy left a comment

👏

Loading

@sikachu
Copy link
Member

@sikachu sikachu commented Mar 28, 2018

@derekprior would you mind fixing the documentation issue? We should be able to merge it in afterward.

Loading

@derekprior derekprior force-pushed the dp-deprecate-force-ssl branch from ca3122c to 1296090 Mar 29, 2018
@derekprior
Copy link
Contributor Author

@derekprior derekprior commented Mar 29, 2018

@sikachu Docs updated. I added :nodoc: as requested but also left the comments I had left on the module for future code-spelunkers. Let me know if I should just remove that.

Loading

Copy link
Member

@sikachu sikachu left a comment

Yep, that's fine! I want to leave the comment there for original code spelunker as well, just want it to disappear from the doc.

It looks like you need to do :nodoc: at the end of the class instead. Do you mind checking my diff?

Loading

# transferred safely over the internet. You _should_ always force the browser
# to use HTTPS when you're transferring sensitive information such as
# user authentication, account information, or credit card information.
# :nodoc:
Copy link
Member

@sikachu sikachu Mar 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this, and it actually didn't remove this from the documentation. Looks like you need to do explicit :nodoc: next to each class:

diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index 4b54b4a19e..b73d54edb3 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -4,12 +4,11 @@
 require "active_support/core_ext/hash/slice"
 
 module ActionController
-  # :nodoc:
   # This module is deprecated in favor of +config.force_ssl+ in your environment
   # config file. This will ensure all communication to non-whitelisted endpoints
   # served by your application occurs over HTTPS.
   #
-  module ForceSSL
+  module ForceSSL # :nodoc:
     extend ActiveSupport::Concern
     include AbstractController::Callbacks
 
@@ -17,7 +16,7 @@ module ForceSSL
     URL_OPTIONS = [:protocol, :host, :domain, :subdomain, :port, :path]
     REDIRECT_OPTIONS = [:status, :flash, :alert, :notice]
 
-    module ClassMethods
+    module ClassMethods # :nodoc:
       def force_ssl(options = {})
         ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
           Controller-level `force_ssl` is deprecated and will be removed from

Loading

Copy link
Contributor Author

@derekprior derekprior Mar 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, updated.

Loading

Today there are two common ways for Rails developers to force their
applications to communicate over HTTPS:

* `config.force_ssl` is a setting in environment configurations that
  enables the `ActionDispatch::SSL` middleware. With this middleware
  enabled, all HTTP communication to your application will be redirected
  to HTTPS. The middleware also takes care of other best practices by
  setting HSTS headers, upgrading all cookies to secure only, etc.
* The `force_ssl` controller method redirects HTTP requests to certain
  controllers to HTTPS.

As a consultant, I've seen many applications with misconfigured HTTPS
setups due to developers adding `force_ssl` to `ApplicationController`
and not enabling `config.force_ssl`. With this configuration, many
application requests can be served over HTTP such as assets, requests
that hit mounted engines, etc. In addition, because cookies are not
upgraded to secure only in this configuration and HSTS headers are not
set, it's possible for cookies that are meant to be secure to be sent
over HTTP.

The confusion between these two methods of forcing HTTPS is compounded
by the fact that they share an identical name. This makes finding
documentation on the "right" method confusing.

HTTPS throughout is quickly becomming table stakes for all web sites.
Sites are expected to operate over HTTPS for all communication,
sensitive or otherwise. Let's encourage use of the broader-reaching
`ActionDispatch::SSL` middleware and elminate this source of user
confusion. If, for some reason, applications need to expose certain
endpoints over HTTP they can do so by properly configuring
`config.ssl_options`.
@derekprior derekprior force-pushed the dp-deprecate-force-ssl branch from 1296090 to 4701a50 Mar 30, 2018
Copy link
Member

@sikachu sikachu left a comment

🚀

Loading

@guilleiguaran guilleiguaran merged commit c680080 into rails:master Mar 30, 2018
1 of 2 checks passed
Loading
@frenkel
Copy link
Contributor

@frenkel frenkel commented Mar 6, 2019

Anybody have an idea how to allow Google Load Balancer health check with config.force_ssl? They always connect over HTTP and will thus get a redirect. Currently we can work around this by using force_ssl in the ApplicationController.

Loading

@yskkin
Copy link
Contributor

@yskkin yskkin commented Mar 6, 2019

I use this for AWS's ALB

  config.force_ssl = true
  config.ssl_options = {
    redirect: {
      exclude: ->(request) { request.env["HTTP_X_FORWARDED_FOR"].blank? }
    }
  }

Loading

@frenkel
Copy link
Contributor

@frenkel frenkel commented Mar 6, 2019

Great, thank you very much!

Loading

@richardonrails
Copy link

@richardonrails richardonrails commented Jan 13, 2020

This change will make it harder to use HTTPS on Rails apps that offer custom domains to certain customers, when certificate generation is not automatic for everyone. Obviously it's possible to generate certs for every custom domain automatically, but it's not necessarily trivial.

In other words, controller-level decision to use HTTPS (based on a database lookup for the hostname specified) seems like a reasonable thing to want to do in some circumstances. Is there any workaround to this?

Loading

@derekprior
Copy link
Contributor Author

@derekprior derekprior commented Jan 16, 2020

You can use config.ssl_options to exclude based on anything that is available to you in the Request. See: https://api.rubyonrails.org/classes/ActionDispatch/SSL.html

So you could exclude based on host, path, headers, whatever...

Loading

@richardonrails
Copy link

@richardonrails richardonrails commented Jan 16, 2020

I saw that... but all the examples were extremely trivial like excluding one path. But I guess you're saying there's nothing stopping me from doing a Model/database lookup based on the host via exclude too?

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet