Skip to content

Commit 4ace047

Browse files
committed
Add back Rack::Runtime to the default middleware stack.
We were planning to remove this middleware because we thought it could make easier to attacker to do a Time Attack. However, while Rack::Runtime can indeed be used to know how long a request took, and compare with other requests, it doesn't provide any information that can't be found in the total time of the request as well. Instead of removing the middleware, we decided to keep it, and direct users to instead of removing it, use its information to uncover actions that are vulnerable to Time Attack. This reverts commit 127dd06, reversing changes made to 4354e3a.
1 parent d177551 commit 4ace047

File tree

9 files changed

+29
-71
lines changed

9 files changed

+29
-71
lines changed

actionpack/lib/action_dispatch/middleware/stack.rb

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,6 @@
55

66
module ActionDispatch
77
class MiddlewareStack
8-
class FakeRuntime # :nodoc:
9-
def initialize(app)
10-
@app = app
11-
end
12-
13-
def call(env)
14-
@app.call(env)
15-
end
16-
end
17-
188
class Middleware
199
attr_reader :args, :block, :klass
2010

@@ -79,7 +69,6 @@ def call(env)
7969

8070
def initialize(*args)
8171
@middlewares = []
82-
@rack_runtime_deprecated = true
8372
yield(self) if block_given?
8473
end
8574

@@ -181,31 +170,13 @@ def build(app = nil, &block)
181170

182171
private
183172
def assert_index(index, where)
184-
i = index.is_a?(Integer) ? index : index_of(index)
173+
i = index.is_a?(Integer) ? index : middlewares.index { |m| m.klass == index }
185174
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
186175
i
187176
end
188177

189178
def build_middleware(klass, args, block)
190-
@rack_runtime_deprecated = false if klass == Rack::Runtime
191-
192179
Middleware.new(klass, args, block)
193180
end
194-
195-
def index_of(klass)
196-
raise "ActionDispatch::MiddlewareStack::FakeRuntime can not be referenced in middleware operations" if klass == FakeRuntime
197-
198-
if klass == Rack::Runtime && @rack_runtime_deprecated
199-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
200-
Rack::Runtime is removed from the default middleware stack in Rails
201-
and referencing it in middleware operations without adding it back
202-
is deprecated and will throw an error in Rails 7.1
203-
MSG
204-
end
205-
206-
middlewares.index do |m|
207-
m.name == klass.name || (@rack_runtime_deprecated && m.klass == FakeRuntime && klass == Rack::Runtime)
208-
end
209-
end
210181
end
211182
end

actionpack/test/dispatch/middleware_stack_test.rb

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -213,27 +213,4 @@ def test_delete_works
213213
test "includes a middleware" do
214214
assert_equal true, @stack.include?(ActionDispatch::MiddlewareStack::Middleware.new(BarMiddleware, nil, nil))
215215
end
216-
217-
test "referencing Rack::Runtime is deprecated" do
218-
@stack.use ActionDispatch::MiddlewareStack::FakeRuntime
219-
220-
assert_deprecated(/Rack::Runtime is removed/) do
221-
@stack.insert_after(Rack::Runtime, BazMiddleware)
222-
end
223-
end
224-
225-
test "referencing Rack::Runtime is not deprecated if added" do
226-
assert_not_deprecated do
227-
@stack.use Rack::Runtime
228-
@stack.insert_before(Rack::Runtime, BazMiddleware)
229-
end
230-
end
231-
232-
test "referencing FakeRuntime throws an error" do
233-
@stack.use ActionDispatch::MiddlewareStack::FakeRuntime
234-
235-
assert_raises RuntimeError do
236-
@stack.insert_after ActionDispatch::MiddlewareStack::FakeRuntime, BazMiddleware
237-
end
238-
end
239216
end

guides/source/api_app.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ An API application comes with the following middleware by default:
204204
- `ActionDispatch::Static`
205205
- `ActionDispatch::Executor`
206206
- `ActiveSupport::Cache::Strategy::LocalCache::Middleware`
207+
- `Rack::Runtime`
207208
- `ActionDispatch::RequestId`
208209
- `ActionDispatch::RemoteIp`
209210
- `Rails::Rack::Logger`

guides/source/command_line.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ Ruby version 2.7.0 (x86_64-linux)
446446
RubyGems version 2.7.3
447447
Rack version 2.0.4
448448
JavaScript Runtime Node.js (V8)
449-
Middleware: Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, ActiveSupport::Cache::Strategy::LocalCache::Middleware, Rack::MethodOverride, ActionDispatch::RequestId, ActionDispatch::RemoteIp, Sprockets::Rails::QuietAssets, Rails::Rack::Logger, ActionDispatch::ShowExceptions, WebConsole::Middleware, ActionDispatch::DebugExceptions, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag
449+
Middleware: Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, ActiveSupport::Cache::Strategy::LocalCache::Middleware, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, ActionDispatch::RemoteIp, Sprockets::Rails::QuietAssets, Rails::Rack::Logger, ActionDispatch::ShowExceptions, WebConsole::Middleware, ActionDispatch::DebugExceptions, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag
450450
Application root /home/foobar/commandsapp
451451
Environment development
452452
Database adapter sqlite3

guides/source/configuring.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,10 @@ Allows thread safe code reloading. Disabled if `config.allow_concurrency` is `fa
414414

415415
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.
416416

417+
#### `Rack::Runtime`
418+
419+
Sets an `X-Runtime` header, containing the time (in seconds) taken to execute the request.
420+
417421
#### `Rails::Rack::Logger`
418422

419423
Notifies the logs that the request has begun. After request is complete, flushes all the logs.
@@ -1503,7 +1507,7 @@ The image analyzers can extract width and height of an image blob; the video ana
15031507

15041508
#### `config.active_storage.previewers`
15051509

1506-
Accepts an array of classes indicating the image previewers available in Active Storage blobs.
1510+
Accepts an array of classes indicating the image previewers available in Active Storage blobs.
15071511
By default, this is defined as:
15081512

15091513
```ruby
@@ -2212,7 +2216,7 @@ Below is a comprehensive list of all the initializers found in Rails in the orde
22122216

22132217
* `initialize_logger`: Initializes the logger (an `ActiveSupport::Logger` object) for the application and makes it accessible at `Rails.logger`, provided that no initializer inserted before this point has defined `Rails.logger`.
22142218

2215-
* `initialize_cache`: If `Rails.cache` isn't set yet, initializes the cache by referencing the value in `config.cache_store` and stores the outcome as `Rails.cache`. If this object responds to the `middleware` method, its middleware is inserted after `ActionDispatch::Executor` in the middleware stack.
2219+
* `initialize_cache`: If `Rails.cache` isn't set yet, initializes the cache by referencing the value in `config.cache_store` and stores the outcome as `Rails.cache`. If this object responds to the `middleware` method, its middleware is inserted before `Rack::Runtime` in the middleware stack.
22162220
22172221
* `set_clear_dependencies_hook`: This initializer - which runs only if `cache_classes` is set to `false` - uses `ActionDispatch::Callbacks.after` to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request.
22182222

guides/source/rails_on_rack.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ use Rack::Sendfile
107107
use ActionDispatch::Static
108108
use ActionDispatch::Executor
109109
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
110+
use Rack::Runtime
110111
use Rack::MethodOverride
111112
use ActionDispatch::RequestId
112113
use ActionDispatch::RemoteIp
@@ -174,10 +175,10 @@ Add the following lines to your application configuration:
174175

175176
```ruby
176177
# config/application.rb
177-
config.middleware.delete ActionDispatch::Executor
178+
config.middleware.delete Rack::Runtime
178179
```
179180

180-
And now if you inspect the middleware stack, you'll find that `ActionDispatch::Executor` is
181+
And now if you inspect the middleware stack, you'll find that `Rack::Runtime` is
181182
not a part of it.
182183

183184
```bash
@@ -236,6 +237,10 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol
236237

237238
* Used for memory caching. This cache is not thread safe.
238239

240+
**`Rack::Runtime`**
241+
242+
* Sets an X-Runtime header, containing the time (in seconds) taken to execute the request.
243+
239244
**`Rack::MethodOverride`**
240245

241246
* Allows the method to be overridden if `params[:_method]` is set. This is the middleware which supports the PUT and DELETE HTTP method types.

railties/lib/rails/application/bootstrap.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ module Bootstrap
5959
Rails.cache = ActiveSupport::Cache.lookup_store(*config.cache_store)
6060

6161
if Rails.cache.respond_to?(:middleware)
62-
config.middleware.insert_after(ActionDispatch::Executor, Rails.cache.middleware)
62+
config.middleware.insert_before(::Rack::Runtime, Rails.cache.middleware)
6363
end
6464
end
6565
end

railties/lib/rails/application/default_middleware_stack.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def build_stack
4242

4343
middleware.use ::ActionDispatch::Executor, app.executor
4444

45-
middleware.use ::ActionDispatch::MiddlewareStack::FakeRuntime
45+
middleware.use ::Rack::Runtime
4646
middleware.use ::Rack::MethodOverride unless config.api_only
4747
middleware.use ::ActionDispatch::RequestId, header: config.action_dispatch.request_id_header
4848
middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies

railties/test/application/middleware_test.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def app
3030
"ActionDispatch::Static",
3131
"ActionDispatch::Executor",
3232
"ActiveSupport::Cache::Strategy::LocalCache",
33-
"ActionDispatch::MiddlewareStack::FakeRuntime",
33+
"Rack::Runtime",
3434
"Rack::MethodOverride",
3535
"ActionDispatch::RequestId",
3636
"ActionDispatch::RemoteIp",
@@ -64,7 +64,7 @@ def app
6464
"ActionDispatch::Static",
6565
"ActionDispatch::Executor",
6666
"ActiveSupport::Cache::Strategy::LocalCache",
67-
"ActionDispatch::MiddlewareStack::FakeRuntime",
67+
"Rack::Runtime",
6868
"Rack::MethodOverride",
6969
"ActionDispatch::RequestId",
7070
"ActionDispatch::RemoteIp",
@@ -98,7 +98,7 @@ def app
9898
"ActionDispatch::Static",
9999
"ActionDispatch::Executor",
100100
"ActiveSupport::Cache::Strategy::LocalCache",
101-
"ActionDispatch::MiddlewareStack::FakeRuntime",
101+
"Rack::Runtime",
102102
"ActionDispatch::RequestId",
103103
"ActionDispatch::RemoteIp",
104104
"Rails::Rack::Logger",
@@ -222,19 +222,19 @@ def app
222222
end
223223

224224
test "can delete a middleware from the stack even if insert_before is added after delete" do
225-
add_to_config "config.middleware.delete Rack::Head"
226-
add_to_config "config.middleware.insert_before(Rack::Head, Rack::Config)"
225+
add_to_config "config.middleware.delete Rack::Runtime"
226+
add_to_config "config.middleware.insert_before(Rack::Runtime, Rack::Config)"
227227
boot!
228228
assert_includes middleware, "Rack::Config"
229-
assert_not middleware.include?("Rack::Head")
229+
assert_not middleware.include?("Rack::Runtime")
230230
end
231231

232232
test "can delete a middleware from the stack even if insert_after is added after delete" do
233-
add_to_config "config.middleware.delete Rack::Head"
234-
add_to_config "config.middleware.insert_after(Rack::Head, Rack::Config)"
233+
add_to_config "config.middleware.delete Rack::Runtime"
234+
add_to_config "config.middleware.insert_after(Rack::Runtime, Rack::Config)"
235235
boot!
236236
assert_includes middleware, "Rack::Config"
237-
assert_not middleware.include?("Rack::Head")
237+
assert_not middleware.include?("Rack::Runtime")
238238
end
239239

240240
test "includes exceptions middlewares even if action_dispatch.show_exceptions is disabled" do
@@ -272,14 +272,14 @@ def app
272272
test "Rails.cache does not respond to middleware" do
273273
add_to_config "config.cache_store = :memory_store, { timeout: 10 }"
274274
boot!
275-
assert_equal "ActionDispatch::MiddlewareStack::FakeRuntime", middleware[4]
275+
assert_equal "Rack::Runtime", middleware[4]
276276
assert_instance_of ActiveSupport::Cache::MemoryStore, Rails.cache
277277
end
278278

279279
test "Rails.cache does respond to middleware" do
280280
boot!
281281
assert_equal "ActiveSupport::Cache::Strategy::LocalCache", middleware[4]
282-
assert_equal "ActionDispatch::MiddlewareStack::FakeRuntime", middleware[5]
282+
assert_equal "Rack::Runtime", middleware[5]
283283
end
284284

285285
test "insert middleware before" do

0 commit comments

Comments
 (0)