Skip to content
This repository
Browse code

Add `config.force_ssl` configuration which will load `Rack::SSL` midd…

…leware if set to true

This will allow user to be able to force all requests to be under HTTPS protocol.

This commit was a request from DHH. Special thanks to Josh Peek as well for making `Rack::SSL`.
  • Loading branch information...
commit 2c0c4d754e34b13379dfc53121a970c25fab5dae 1 parent b2d9432
Prem Sichanugrist authored March 27, 2011 dhh committed March 27, 2011
2  railties/CHANGELOG
... ...
@@ -1,5 +1,7 @@
1 1
 *Rails 3.1.0 (unreleased)*
2 2
 
  3
+* Added `config.force_ssl` configuration which loads Rack::SSL middleware and force all requests to be under HTTPS protocol [DHH, Prem Sichanugrist, and Josh Peek]
  4
+
3 5
 * Added `rails plugin new` command which generates rails plugin with gemspec, tests and dummy application for testing [Piotr Sarnacki]
4 6
 
5 7
 * Added -j parameter with jquery/prototype as options. Now you can create your apps with jQuery using `rails new myapp -j jquery`. The default is still Prototype. [siong1987]
3  railties/guides/source/configuring.textile
Source Rendered
@@ -81,6 +81,8 @@ end
81 81
 
82 82
 * +config.filter_parameters+ used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers.
83 83
 
  84
+* +config.force_ssl+ forcing all requests to be under HTTPS protocol by using +Rack::SSL+ middleware. This will secure your application from a session hijack attempt.
  85
+
84 86
 * +config.helper_paths+ configures where Rails can find helpers for this application.
85 87
 
86 88
 * +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+.
@@ -147,6 +149,7 @@ h4. Configuring Middleware
147 149
 
148 150
 Every Rails application comes with a standard set of middleware which it uses in this order in the development environment:
149 151
 
  152
+* +Rack::SSL+ Will force every requests to be under HTTPS protocal. Will be available if +config.force_ssl+ is set to _true_.
150 153
 * +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is _true_.
151 154
 * +Rack::Lock+ Will wrap the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to _false_, which it is by default.
152 155
 * +ActiveSupport::Cache::Strategy::LocalCache+ Serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread.
6  railties/guides/source/security.textile
Source Rendered
@@ -57,7 +57,11 @@ Many web applications have an authentication system: a user provides a user name
57 57
 
58 58
 Hence, the cookie serves as temporary authentication for the web application. Everyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures:
59 59
 
60  
-* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to _(highlight)provide a secure connection over SSL_.
  60
+* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to _(highlight)provide a secure connection over SSL_. In Rails 3.1 and later, this could be accomplished by always forcing SSL connection in your application config file:
  61
+
  62
+<ruby>
  63
+config.force_ssl = true
  64
+</ruby>
61 65
 
62 66
 * Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a _(highlight)log-out button_ in the web application, and _(highlight)make it prominent_.
63 67
 
17  railties/lib/rails/application.rb
@@ -145,15 +145,21 @@ def default_asset_path
145 145
 
146 146
     def default_middleware_stack
147 147
       ActionDispatch::MiddlewareStack.new.tap do |middleware|
148  
-        rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
  148
+        if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
  149
+          require "action_dispatch/http/rack_cache"
  150
+          middleware.use ::Rack::Cache, rack_cache
  151
+        end
149 152
 
150  
-        require "action_dispatch/http/rack_cache" if rack_cache
151  
-        middleware.use ::Rack::Cache, rack_cache  if rack_cache
  153
+        if config.force_ssl
  154
+          require "rack/ssl"
  155
+          middleware.use ::Rack::SSL
  156
+        end
152 157
 
153 158
         if config.serve_static_assets
154 159
           asset_paths = ActiveSupport::OrderedHash[config.static_asset_paths.to_a.reverse]
155 160
           middleware.use ::ActionDispatch::Static, asset_paths
156 161
         end
  162
+
157 163
         middleware.use ::Rack::Lock unless config.allow_concurrency
158 164
         middleware.use ::Rack::Runtime
159 165
         middleware.use ::Rails::Rack::Logger
@@ -174,7 +180,10 @@ def default_middleware_stack
174 180
         middleware.use ::ActionDispatch::Head
175 181
         middleware.use ::Rack::ConditionalGet
176 182
         middleware.use ::Rack::ETag, "no-cache"
177  
-        middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support if config.action_dispatch.best_standards_support
  183
+
  184
+        if config.action_dispatch.best_standards_support
  185
+          middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support
  186
+        end
178 187
       end
179 188
     end
180 189
 
3  railties/lib/rails/application/configuration.rb
@@ -9,7 +9,7 @@ class Configuration < ::Rails::Engine::Configuration
9 9
                     :filter_parameters, :helpers_paths, :logger,
10 10
                     :preload_frameworks, :reload_plugins,
11 11
                     :secret_token, :serve_static_assets, :session_options,
12  
-                    :time_zone, :whiny_nils
  12
+                    :time_zone, :whiny_nils, :force_ssl
13 13
 
14 14
       attr_writer :log_level
15 15
 
@@ -22,6 +22,7 @@ def initialize(*)
22 22
         @helpers_paths               = []
23 23
         @dependency_loading          = true
24 24
         @serve_static_assets         = true
  25
+        @force_ssl                   = false
25 26
         @session_store               = :cookie_store
26 27
         @session_options             = {}
27 28
         @time_zone                   = "UTC"
2  railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -3,7 +3,7 @@
3 3
 
4 4
   # In the development environment your application's code is reloaded on
5 5
   # every request.  This slows down response time but is perfect for development
6  
-  # since you don't have to restart the webserver when you make code changes.
  6
+  # since you don't have to restart the web server when you make code changes.
7 7
   config.cache_classes = false
8 8
 
9 9
   # Log error messages when you accidentally call methods on nil.
18  railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
... ...
@@ -1,7 +1,6 @@
1 1
 <%= app_const %>.configure do
2 2
   # Settings specified here will take precedence over those in config/application.rb
3 3
 
4  
-  # The production environment is meant for finished, "live" apps.
5 4
   # Code is not reloaded between requests
6 5
   config.cache_classes = true
7 6
 
@@ -9,14 +8,15 @@
9 8
   config.consider_all_requests_local       = false
10 9
   config.action_controller.perform_caching = true
11 10
 
12  
-  # Specifies the header that your server uses for sending files
13  
-  config.action_dispatch.x_sendfile_header = "X-Sendfile"
  11
+  # Disable Rails's static asset server (Apache or nginx will already do this)
  12
+  config.serve_static_assets = false
14 13
 
15  
-  # For nginx:
16  
-  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
  14
+  # Specifies the header that your server uses for sending files
  15
+  # (comment out if your front-end server doesn't support this)
  16
+  config.action_dispatch.x_sendfile_header = "X-Sendfile" # Use 'X-Accel-Redirect' for nginx
17 17
 
18  
-  # If you have no front-end server that supports something like X-Sendfile,
19  
-  # just comment this out and Rails will serve the files
  18
+  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  19
+  # config.force_ssl = true
20 20
 
21 21
   # See everything in the log (default is :info)
22 22
   # config.log_level = :debug
@@ -27,10 +27,6 @@
27 27
   # Use a different cache store in production
28 28
   # config.cache_store = :mem_cache_store
29 29
 
30  
-  # Disable Rails's static asset server
31  
-  # In production, Apache or nginx will already do this
32  
-  config.serve_static_assets = false
33  
-
34 30
   # Enable serving of images, stylesheets, and javascripts from an asset server
35 31
   # config.action_controller.asset_host = "http://assets.example.com"
36 32
 
1  railties/railties.gemspec
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21 21
 
22 22
   s.add_dependency('rake',          '>= 0.8.7')
23 23
   s.add_dependency('thor',          '~> 0.14.4')
  24
+  s.add_dependency('rack-ssl',      '~> 1.3.2')
24 25
   s.add_dependency('activesupport', version)
25 26
   s.add_dependency('actionpack',    version)
26 27
 end
6  railties/test/application/middleware_test.rb
@@ -52,6 +52,12 @@ def app
52 52
       assert_equal "Rack::Cache", middleware.first
53 53
     end
54 54
 
  55
+    test "Rack::SSL is present with force_ssl is set" do
  56
+      add_to_config "config.force_ssl = true"
  57
+      boot!
  58
+      assert middleware.include?("Rack::SSL")
  59
+    end
  60
+
55 61
     test "removing Active Record omits its middleware" do
56 62
       use_frameworks []
57 63
       boot!

5 notes on commit 2c0c4d7

Jonas Grimfelt

Good! This really makes sense nowadays, SSL is a bit of a pain for beginners even though it is becoming a high standards practice on the web.

Andy Lindeman

Awesome!

Neeraj Singh
Collaborator

Nice job Prem.

David Heinemeier Hansson
Owner
dhh commented on 2c0c4d7 March 28, 2011

@josh can you review if all this is proper?

Jesse Storimer

It's nice to have this option available as a one-liner, but given the fact that Rack::SSL is already so easy to integrate, it seems strange to make ALL rails apps depend on this gem, regardless of whether or not they use this option. Why not just put it in the default Gemfile?

Jesse Storimer

This seems to me like a perfect case for Railtie. I've encapsulated my thoughts into a library: https://github.com/jstorimer/rack-ssl-rails. See the README for more of my reasoning.

Nick Quaranto
qrush commented on 2c0c4d7 July 06, 2011

Woot! :D

Please sign in to comment.
Something went wrong with that request. Please try again.