actionpack 7.1.3 → 7.2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -501
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +102 -98
- data/lib/abstract_controller/caching/fragments.rb +50 -53
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +66 -64
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +70 -85
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +13 -12
- data/lib/abstract_controller/translation.rb +15 -7
- data/lib/abstract_controller/url_for.rb +8 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +74 -72
- data/lib/action_controller/base.rb +198 -126
- data/lib/action_controller/caching.rb +15 -12
- data/lib/action_controller/deprecator.rb +2 -0
- data/lib/action_controller/form_builder.rb +20 -17
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +188 -174
- data/lib/action_controller/metal/content_security_policy.rb +25 -24
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +64 -55
- data/lib/action_controller/metal/default_headers.rb +5 -3
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
- data/lib/action_controller/metal/exceptions.rb +11 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +12 -10
- data/lib/action_controller/metal/helpers.rb +63 -55
- data/lib/action_controller/metal/http_authentication.rb +210 -205
- data/lib/action_controller/metal/implicit_render.rb +17 -15
- data/lib/action_controller/metal/instrumentation.rb +15 -12
- data/lib/action_controller/metal/live.rb +113 -107
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +57 -59
- data/lib/action_controller/metal/permissions_policy.rb +13 -12
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +108 -82
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -75
- data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
- data/lib/action_controller/metal/rescue.rb +11 -9
- data/lib/action_controller/metal/streaming.rb +138 -136
- data/lib/action_controller/metal/strong_parameters.rb +525 -480
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +17 -15
- data/lib/action_controller/metal.rb +86 -60
- data/lib/action_controller/railtie.rb +3 -0
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +42 -36
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +146 -126
- data/lib/action_controller.rb +10 -3
- data/lib/action_dispatch/constants.rb +2 -0
- data/lib/action_dispatch/deprecator.rb +2 -0
- data/lib/action_dispatch/http/cache.rb +27 -26
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +44 -38
- data/lib/action_dispatch/http/filter_parameters.rb +18 -9
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +22 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +30 -41
- data/lib/action_dispatch/http/mime_type.rb +31 -24
- data/lib/action_dispatch/http/mime_types.rb +2 -0
- data/lib/action_dispatch/http/parameters.rb +11 -9
- data/lib/action_dispatch/http/permissions_policy.rb +20 -44
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +94 -75
- data/lib/action_dispatch/http/response.rb +73 -61
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +13 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +4 -1
- data/lib/action_dispatch/journey/route.rb +9 -7
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +4 -2
- data/lib/action_dispatch/journey/routes.rb +4 -2
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +2 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
- data/lib/action_dispatch/middleware/callbacks.rb +3 -1
- data/lib/action_dispatch/middleware/cookies.rb +119 -104
- data/lib/action_dispatch/middleware/debug_exceptions.rb +13 -5
- data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
- data/lib/action_dispatch/middleware/debug_view.rb +2 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +6 -11
- data/lib/action_dispatch/middleware/executor.rb +8 -0
- data/lib/action_dispatch/middleware/flash.rb +63 -51
- data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
- data/lib/action_dispatch/middleware/public_exceptions.rb +8 -6
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +77 -72
- data/lib/action_dispatch/middleware/request_id.rb +14 -9
- data/lib/action_dispatch/middleware/server_timing.rb +4 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
- data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +31 -21
- data/lib/action_dispatch/middleware/ssl.rb +43 -40
- data/lib/action_dispatch/middleware/stack.rb +11 -10
- data/lib/action_dispatch/middleware/static.rb +33 -31
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +2 -4
- data/lib/action_dispatch/request/session.rb +23 -21
- data/lib/action_dispatch/request/utils.rb +2 -0
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +5 -3
- data/lib/action_dispatch/routing/mapper.rb +671 -636
- data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
- data/lib/action_dispatch/routing/redirection.rb +37 -32
- data/lib/action_dispatch/routing/route_set.rb +59 -45
- data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
- data/lib/action_dispatch/routing/url_for.rb +130 -125
- data/lib/action_dispatch/routing.rb +150 -148
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +10 -3
- data/lib/action_dispatch/system_testing/driver.rb +3 -1
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +8 -6
- data/lib/action_dispatch/testing/assertions/response.rb +26 -23
- data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/integration.rb +223 -222
- data/lib/action_dispatch/testing/request_encoder.rb +2 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +12 -8
- data/lib/action_dispatch/testing/test_request.rb +3 -1
- data/lib/action_dispatch/testing/test_response.rb +27 -26
- data/lib/action_dispatch.rb +22 -28
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +17 -16
- metadata +39 -16
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "rack/session/abstract/id"
|
4
6
|
require "action_controller/metal/exceptions"
|
5
7
|
require "active_support/security_utils"
|
@@ -11,51 +13,53 @@ module ActionController # :nodoc:
|
|
11
13
|
class InvalidCrossOriginRequest < ActionControllerError # :nodoc:
|
12
14
|
end
|
13
15
|
|
14
|
-
#
|
16
|
+
# # Action Controller Request Forgery Protection
|
15
17
|
#
|
16
|
-
# Controller actions are protected from Cross-Site Request Forgery (CSRF)
|
17
|
-
# by including a token in the rendered HTML for your application. This
|
18
|
-
# stored as a random string in the session, to which an attacker does
|
19
|
-
# access. When a request reaches your application,
|
20
|
-
# token with the token in the session. All requests are checked except
|
21
|
-
# as these should be idempotent. Keep in mind that all
|
22
|
-
# are CSRF protected by default, including JavaScript
|
18
|
+
# Controller actions are protected from Cross-Site Request Forgery (CSRF)
|
19
|
+
# attacks by including a token in the rendered HTML for your application. This
|
20
|
+
# token is stored as a random string in the session, to which an attacker does
|
21
|
+
# not have access. When a request reaches your application, Rails verifies the
|
22
|
+
# received token with the token in the session. All requests are checked except
|
23
|
+
# GET requests as these should be idempotent. Keep in mind that all
|
24
|
+
# session-oriented requests are CSRF protected by default, including JavaScript
|
25
|
+
# and HTML requests.
|
23
26
|
#
|
24
27
|
# Since HTML and JavaScript requests are typically made from the browser, we
|
25
|
-
# need to ensure to verify request authenticity for the web browser. We can
|
26
|
-
#
|
27
|
-
#
|
28
|
+
# need to ensure to verify request authenticity for the web browser. We can use
|
29
|
+
# session-oriented authentication for these types of requests, by using the
|
30
|
+
# `protect_from_forgery` method in our controllers.
|
28
31
|
#
|
29
32
|
# GET requests are not protected since they don't have side effects like writing
|
30
33
|
# to the database and don't leak sensitive information. JavaScript requests are
|
31
|
-
# an exception: a third-party site can use a <script> tag to reference a
|
32
|
-
# URL on your site. When your JavaScript response loads on their
|
33
|
-
# With carefully crafted JavaScript on their end, sensitive
|
34
|
-
# response may be extracted. To prevent this, only
|
35
|
-
# Ajax) requests are allowed to make requests
|
34
|
+
# an exception: a third-party site can use a <script> tag to reference a
|
35
|
+
# JavaScript URL on your site. When your JavaScript response loads on their
|
36
|
+
# site, it executes. With carefully crafted JavaScript on their end, sensitive
|
37
|
+
# data in your JavaScript response may be extracted. To prevent this, only
|
38
|
+
# XmlHttpRequest (known as XHR or Ajax) requests are allowed to make requests
|
39
|
+
# for JavaScript responses.
|
36
40
|
#
|
37
41
|
# Subclasses of ActionController::Base are protected by default with the
|
38
|
-
#
|
42
|
+
# `:exception` strategy, which raises an
|
39
43
|
# ActionController::InvalidAuthenticityToken error on unverified requests.
|
40
44
|
#
|
41
45
|
# APIs may want to disable this behavior since they are typically designed to be
|
42
|
-
# state-less: that is, the request API client handles the session instead of
|
43
|
-
# One way to achieve this is to use the
|
46
|
+
# state-less: that is, the request API client handles the session instead of
|
47
|
+
# Rails. One way to achieve this is to use the `:null_session` strategy instead,
|
44
48
|
# which allows unverified requests to be handled, but with an empty session:
|
45
49
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
50
|
+
# class ApplicationController < ActionController::Base
|
51
|
+
# protect_from_forgery with: :null_session
|
52
|
+
# end
|
49
53
|
#
|
50
|
-
# Note that API only applications don't include this module or a session
|
51
|
-
# by default, and so don't require CSRF protection to be configured.
|
54
|
+
# Note that API only applications don't include this module or a session
|
55
|
+
# middleware by default, and so don't require CSRF protection to be configured.
|
52
56
|
#
|
53
|
-
# The token parameter is named
|
54
|
-
# value of this token must be added to every layout that renders forms by
|
55
|
-
#
|
57
|
+
# The token parameter is named `authenticity_token` by default. The name and
|
58
|
+
# value of this token must be added to every layout that renders forms by
|
59
|
+
# including `csrf_meta_tags` in the HTML `head`.
|
56
60
|
#
|
57
|
-
# Learn more about CSRF attacks and securing your application in the
|
58
|
-
#
|
61
|
+
# Learn more about CSRF attacks and securing your application in the [Ruby on
|
62
|
+
# Rails Security Guide](https://guides.rubyonrails.org/security.html).
|
59
63
|
module RequestForgeryProtection
|
60
64
|
CSRF_TOKEN = "action_controller.csrf_token"
|
61
65
|
|
@@ -65,8 +69,8 @@ module ActionController # :nodoc:
|
|
65
69
|
include AbstractController::Callbacks
|
66
70
|
|
67
71
|
included do
|
68
|
-
# Sets the token parameter name for RequestForgery. Calling
|
69
|
-
# sets it to
|
72
|
+
# Sets the token parameter name for RequestForgery. Calling
|
73
|
+
# `protect_from_forgery` sets it to `:authenticity_token` by default.
|
70
74
|
config_accessor :request_forgery_protection_token
|
71
75
|
self.request_forgery_protection_token ||= :authenticity_token
|
72
76
|
|
@@ -74,7 +78,8 @@ module ActionController # :nodoc:
|
|
74
78
|
config_accessor :forgery_protection_strategy
|
75
79
|
self.forgery_protection_strategy = nil
|
76
80
|
|
77
|
-
# Controls whether request forgery protection is turned on or not. Turned off by
|
81
|
+
# Controls whether request forgery protection is turned on or not. Turned off by
|
82
|
+
# default only in test mode.
|
78
83
|
config_accessor :allow_forgery_protection
|
79
84
|
self.allow_forgery_protection = true if allow_forgery_protection.nil?
|
80
85
|
|
@@ -90,10 +95,6 @@ module ActionController # :nodoc:
|
|
90
95
|
config_accessor :per_form_csrf_tokens
|
91
96
|
self.per_form_csrf_tokens = false
|
92
97
|
|
93
|
-
# Controls whether forgery protection is enabled by default.
|
94
|
-
config_accessor :default_protect_from_forgery
|
95
|
-
self.default_protect_from_forgery = false
|
96
|
-
|
97
98
|
# The strategy to use for storing and retrieving CSRF tokens.
|
98
99
|
config_accessor :csrf_token_storage_strategy
|
99
100
|
self.csrf_token_storage_strategy = SessionStore.new
|
@@ -103,79 +104,96 @@ module ActionController # :nodoc:
|
|
103
104
|
end
|
104
105
|
|
105
106
|
module ClassMethods
|
106
|
-
# Turn on request forgery protection. Bear in mind that GET and HEAD requests
|
107
|
+
# Turn on request forgery protection. Bear in mind that GET and HEAD requests
|
108
|
+
# are not checked.
|
107
109
|
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
110
|
+
# class ApplicationController < ActionController::Base
|
111
|
+
# protect_from_forgery
|
112
|
+
# end
|
111
113
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
114
|
+
# class FooController < ApplicationController
|
115
|
+
# protect_from_forgery except: :index
|
116
|
+
# end
|
115
117
|
#
|
116
|
-
# You can disable forgery protection on a controller using
|
118
|
+
# You can disable forgery protection on a controller using
|
119
|
+
# skip_forgery_protection:
|
117
120
|
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
+
# class BarController < ApplicationController
|
122
|
+
# skip_forgery_protection
|
123
|
+
# end
|
121
124
|
#
|
122
125
|
# Valid Options:
|
123
126
|
#
|
124
|
-
# *
|
125
|
-
#
|
126
|
-
# *
|
127
|
-
#
|
128
|
-
#
|
127
|
+
# * `:only` / `:except` - Only apply forgery protection to a subset of
|
128
|
+
# actions. For example `only: [ :create, :create_all ]`.
|
129
|
+
# * `:if` / `:unless` - Turn off the forgery protection entirely depending on
|
130
|
+
# the passed Proc or method reference.
|
131
|
+
# * `:prepend` - By default, the verification of the authentication token will
|
132
|
+
# be added at the position of the protect_from_forgery call in your
|
133
|
+
# application. This means any callbacks added before are run first. This is
|
134
|
+
# useful when you want your forgery protection to depend on other callbacks,
|
135
|
+
# like authentication methods (Oauth vs Cookie auth).
|
136
|
+
#
|
137
|
+
# If you need to add verification to the beginning of the callback chain,
|
138
|
+
# use `prepend: true`.
|
139
|
+
# * `:with` - Set the method to handle unverified request. Note if
|
140
|
+
# `default_protect_from_forgery` is true, Rails call protect_from_forgery
|
141
|
+
# with `with :exception`.
|
129
142
|
#
|
130
|
-
# If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
|
131
|
-
# * <tt>:with</tt> - Set the method to handle unverified request.
|
132
|
-
# Note if <tt>default_protect_from_forgery</tt> is true, Rails call protect_from_forgery with <tt>with :exception</tt>.
|
133
143
|
#
|
134
144
|
# Built-in unverified request handling methods are:
|
135
|
-
# *
|
136
|
-
#
|
137
|
-
# *
|
145
|
+
# * `:exception` - Raises ActionController::InvalidAuthenticityToken
|
146
|
+
# exception.
|
147
|
+
# * `:reset_session` - Resets the session.
|
148
|
+
# * `:null_session` - Provides an empty session during request but doesn't
|
149
|
+
# reset it completely. Used as default if `:with` option is not specified.
|
138
150
|
#
|
139
|
-
# You can also implement custom strategy classes for unverified request handling:
|
140
151
|
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
152
|
+
# You can also implement custom strategy classes for unverified request
|
153
|
+
# handling:
|
154
|
+
#
|
155
|
+
# class CustomStrategy
|
156
|
+
# def initialize(controller)
|
157
|
+
# @controller = controller
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# def handle_unverified_request
|
161
|
+
# # Custom behavior for unverfied request
|
162
|
+
# end
|
163
|
+
# end
|
164
|
+
#
|
165
|
+
# class ApplicationController < ActionController::Base
|
166
|
+
# protect_from_forgery with: CustomStrategy
|
167
|
+
# end
|
145
168
|
#
|
146
|
-
#
|
147
|
-
# # Custom behavior for unverfied request
|
148
|
-
# end
|
149
|
-
# end
|
169
|
+
# * `:store` - Set the strategy to store and retrieve CSRF tokens.
|
150
170
|
#
|
151
|
-
# class ApplicationController < ActionController::Base
|
152
|
-
# protect_from_forgery with: CustomStrategy
|
153
|
-
# end
|
154
|
-
# * <tt>:store</tt> - Set the strategy to store and retrieve CSRF tokens.
|
155
171
|
#
|
156
172
|
# Built-in session token strategies are:
|
157
|
-
# *
|
158
|
-
#
|
173
|
+
# * `:session` - Store the CSRF token in the session. Used as default if
|
174
|
+
# `:store` option is not specified.
|
175
|
+
# * `:cookie` - Store the CSRF token in an encrypted cookie.
|
176
|
+
#
|
159
177
|
#
|
160
178
|
# You can also implement custom strategy classes for CSRF token storage:
|
161
179
|
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
180
|
+
# class CustomStore
|
181
|
+
# def fetch(request)
|
182
|
+
# # Return the token from a custom location
|
183
|
+
# end
|
166
184
|
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
185
|
+
# def store(request, csrf_token)
|
186
|
+
# # Store the token in a custom location
|
187
|
+
# end
|
170
188
|
#
|
171
|
-
#
|
172
|
-
#
|
189
|
+
# def reset(request)
|
190
|
+
# # Delete the stored session token
|
191
|
+
# end
|
173
192
|
# end
|
174
|
-
# end
|
175
193
|
#
|
176
|
-
#
|
177
|
-
#
|
178
|
-
#
|
194
|
+
# class ApplicationController < ActionController::Base
|
195
|
+
# protect_from_forgery store: CustomStore.new
|
196
|
+
# end
|
179
197
|
def protect_from_forgery(options = {})
|
180
198
|
options = options.reverse_merge(prepend: false)
|
181
199
|
|
@@ -190,9 +208,9 @@ module ActionController # :nodoc:
|
|
190
208
|
|
191
209
|
# Turn off request forgery protection. This is a wrapper for:
|
192
210
|
#
|
193
|
-
#
|
211
|
+
# skip_before_action :verify_authenticity_token
|
194
212
|
#
|
195
|
-
# See
|
213
|
+
# See `skip_before_action` for allowed options.
|
196
214
|
def skip_forgery_protection(options = {})
|
197
215
|
skip_before_action :verify_authenticity_token, options.reverse_merge(raise: false)
|
198
216
|
end
|
@@ -236,7 +254,8 @@ module ActionController # :nodoc:
|
|
236
254
|
@controller = controller
|
237
255
|
end
|
238
256
|
|
239
|
-
# This is the method that defines the application behavior when a request is
|
257
|
+
# This is the method that defines the application behavior when a request is
|
258
|
+
# found to be unverified.
|
240
259
|
def handle_unverified_request
|
241
260
|
request = @controller.request
|
242
261
|
request.session = NullSessionHash.new(request)
|
@@ -246,7 +265,7 @@ module ActionController # :nodoc:
|
|
246
265
|
end
|
247
266
|
|
248
267
|
private
|
249
|
-
class NullSessionHash < Rack::Session::Abstract::SessionHash
|
268
|
+
class NullSessionHash < Rack::Session::Abstract::SessionHash
|
250
269
|
def initialize(req)
|
251
270
|
super(nil, req)
|
252
271
|
@data = {}
|
@@ -265,7 +284,7 @@ module ActionController # :nodoc:
|
|
265
284
|
end
|
266
285
|
end
|
267
286
|
|
268
|
-
class NullCookieJar < ActionDispatch::Cookies::CookieJar
|
287
|
+
class NullCookieJar < ActionDispatch::Cookies::CookieJar
|
269
288
|
def write(*)
|
270
289
|
# nothing
|
271
290
|
end
|
@@ -344,7 +363,7 @@ module ActionController # :nodoc:
|
|
344
363
|
|
345
364
|
def initialize(...)
|
346
365
|
super
|
347
|
-
@
|
366
|
+
@_marked_for_same_origin_verification = nil
|
348
367
|
end
|
349
368
|
|
350
369
|
def reset_csrf_token(request) # :doc:
|
@@ -358,16 +377,15 @@ module ActionController # :nodoc:
|
|
358
377
|
end
|
359
378
|
|
360
379
|
private
|
361
|
-
# The actual before_action that is used to verify the CSRF token.
|
362
|
-
#
|
363
|
-
#
|
364
|
-
# <tt><script></tt> verification.
|
380
|
+
# The actual before_action that is used to verify the CSRF token. Don't override
|
381
|
+
# this directly. Provide your own forgery protection strategy instead. If you
|
382
|
+
# override, you'll disable same-origin `<script>` verification.
|
365
383
|
#
|
366
|
-
# Lean on the protect_from_forgery declaration to mark which actions are
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
370
|
-
#
|
384
|
+
# Lean on the protect_from_forgery declaration to mark which actions are due for
|
385
|
+
# same-origin request verification. If protect_from_forgery is enabled on an
|
386
|
+
# action, this before_action flags its after_action to verify that JavaScript
|
387
|
+
# responses are for XHR requests, ensuring they follow the browser's same-origin
|
388
|
+
# policy.
|
371
389
|
def verify_authenticity_token # :doc:
|
372
390
|
mark_for_same_origin_verification!
|
373
391
|
|
@@ -378,7 +396,7 @@ module ActionController # :nodoc:
|
|
378
396
|
end
|
379
397
|
end
|
380
398
|
|
381
|
-
def handle_unverified_request
|
399
|
+
def handle_unverified_request
|
382
400
|
protection_strategy = forgery_protection_strategy.new(self)
|
383
401
|
|
384
402
|
if protection_strategy.respond_to?(:warning_message)
|
@@ -388,7 +406,7 @@ module ActionController # :nodoc:
|
|
388
406
|
protection_strategy.handle_unverified_request
|
389
407
|
end
|
390
408
|
|
391
|
-
def unverified_request_warning_message
|
409
|
+
def unverified_request_warning_message
|
392
410
|
if valid_request_origin?
|
393
411
|
"Can't verify CSRF token authenticity."
|
394
412
|
else
|
@@ -396,7 +414,6 @@ module ActionController # :nodoc:
|
|
396
414
|
end
|
397
415
|
end
|
398
416
|
|
399
|
-
# :nodoc:
|
400
417
|
CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
|
401
418
|
"<script> tag on another site requested protected JavaScript. " \
|
402
419
|
"If you know what you're doing, go ahead and disable forgery " \
|
@@ -404,9 +421,9 @@ module ActionController # :nodoc:
|
|
404
421
|
private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
|
405
422
|
# :startdoc:
|
406
423
|
|
407
|
-
# If
|
408
|
-
# forgery protection enabled for this request) then also verify that
|
409
|
-
#
|
424
|
+
# If `verify_authenticity_token` was run (indicating that we have
|
425
|
+
# forgery protection enabled for this request) then also verify that we aren't
|
426
|
+
# serving an unauthorized cross-origin response.
|
410
427
|
def verify_same_origin_request # :doc:
|
411
428
|
if marked_for_same_origin_verification? && non_xhr_javascript_response?
|
412
429
|
if logger && log_warning_on_csrf_failure
|
@@ -418,13 +435,13 @@ module ActionController # :nodoc:
|
|
418
435
|
|
419
436
|
# GET requests are checked for cross-origin JavaScript after rendering.
|
420
437
|
def mark_for_same_origin_verification! # :doc:
|
421
|
-
@
|
438
|
+
@_marked_for_same_origin_verification = request.get?
|
422
439
|
end
|
423
440
|
|
424
|
-
# If the
|
425
|
-
#
|
441
|
+
# If the `verify_authenticity_token` before_action ran, verify that JavaScript
|
442
|
+
# responses are only served to same-origin GET requests.
|
426
443
|
def marked_for_same_origin_verification? # :doc:
|
427
|
-
@
|
444
|
+
@_marked_for_same_origin_verification ||= false
|
428
445
|
end
|
429
446
|
|
430
447
|
# Check for cross-origin JavaScript responses.
|
@@ -436,9 +453,11 @@ module ActionController # :nodoc:
|
|
436
453
|
|
437
454
|
# Returns true or false if a request is verified. Checks:
|
438
455
|
#
|
439
|
-
# *
|
440
|
-
# *
|
441
|
-
#
|
456
|
+
# * Is it a GET or HEAD request? GETs should be safe and idempotent
|
457
|
+
# * Does the form_authenticity_token match the given token value from the
|
458
|
+
# params?
|
459
|
+
# * Does the `X-CSRF-Token` header match the form_authenticity_token?
|
460
|
+
#
|
442
461
|
def verified_request? # :doc:
|
443
462
|
!protect_against_forgery? || request.get? || request.head? ||
|
444
463
|
(valid_request_origin? && any_authenticity_token_valid?)
|
@@ -461,9 +480,8 @@ module ActionController # :nodoc:
|
|
461
480
|
masked_authenticity_token(form_options: form_options)
|
462
481
|
end
|
463
482
|
|
464
|
-
# Creates a masked version of the authenticity token that varies
|
465
|
-
#
|
466
|
-
# like BREACH.
|
483
|
+
# Creates a masked version of the authenticity token that varies on each
|
484
|
+
# request. The masking is used to mitigate SSL attacks like BREACH.
|
467
485
|
def masked_authenticity_token(form_options: {})
|
468
486
|
action, method = form_options.values_at(:action, :method)
|
469
487
|
|
@@ -477,9 +495,8 @@ module ActionController # :nodoc:
|
|
477
495
|
mask_token(raw_token)
|
478
496
|
end
|
479
497
|
|
480
|
-
# Checks the client's masked token to see if it matches the
|
481
|
-
#
|
482
|
-
# +masked_authenticity_token+.
|
498
|
+
# Checks the client's masked token to see if it matches the session token.
|
499
|
+
# Essentially the inverse of `masked_authenticity_token`.
|
483
500
|
def valid_authenticity_token?(session, encoded_masked_token) # :doc:
|
484
501
|
if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
|
485
502
|
return false
|
@@ -491,14 +508,12 @@ module ActionController # :nodoc:
|
|
491
508
|
return false
|
492
509
|
end
|
493
510
|
|
494
|
-
# See if it's actually a masked token or not. In order to
|
495
|
-
#
|
496
|
-
# tokens that we've issued without error.
|
511
|
+
# See if it's actually a masked token or not. In order to deploy this code, we
|
512
|
+
# should be able to handle any unmasked tokens that we've issued without error.
|
497
513
|
|
498
514
|
if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
|
499
|
-
# This is actually an unmasked token. This is expected if
|
500
|
-
#
|
501
|
-
# happening shortly after installing this gem.
|
515
|
+
# This is actually an unmasked token. This is expected if you have just upgraded
|
516
|
+
# to masked tokens, but should stop happening shortly after installing this gem.
|
502
517
|
compare_with_real_token masked_token
|
503
518
|
|
504
519
|
elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
|
@@ -513,8 +528,7 @@ module ActionController # :nodoc:
|
|
513
528
|
end
|
514
529
|
|
515
530
|
def unmask_token(masked_token) # :doc:
|
516
|
-
# Split the token into the one-time pad and the encrypted
|
517
|
-
# value and decrypt it.
|
531
|
+
# Split the token into the one-time pad and the encrypted value and decrypt it.
|
518
532
|
one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
|
519
533
|
encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
|
520
534
|
xor_byte_strings(one_time_pad, encrypted_csrf_token)
|
@@ -606,8 +620,8 @@ module ActionController # :nodoc:
|
|
606
620
|
Rails.application.config.action_controller.forgery_protection_origin_check setting.
|
607
621
|
MSG
|
608
622
|
|
609
|
-
# Checks if the request originated from the same origin by looking at the
|
610
|
-
#
|
623
|
+
# Checks if the request originated from the same origin by looking at the Origin
|
624
|
+
# header.
|
611
625
|
def valid_request_origin? # :doc:
|
612
626
|
if forgery_protection_origin_check
|
613
627
|
# We accept blank origin headers because some user agents don't send it.
|
@@ -620,18 +634,33 @@ module ActionController # :nodoc:
|
|
620
634
|
|
621
635
|
def normalize_action_path(action_path) # :doc:
|
622
636
|
uri = URI.parse(action_path)
|
637
|
+
|
638
|
+
if uri.relative? && (action_path.blank? || !action_path.start_with?("/"))
|
639
|
+
normalize_relative_action_path(uri.path)
|
640
|
+
else
|
641
|
+
uri.path.chomp("/")
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
def normalize_relative_action_path(rel_action_path) # :doc:
|
646
|
+
uri = URI.parse(request.path)
|
647
|
+
# add the action path to the request.path
|
648
|
+
uri.path += "/#{rel_action_path}"
|
649
|
+
# relative path with "./path"
|
650
|
+
uri.path.gsub!("/./", "/")
|
651
|
+
|
623
652
|
uri.path.chomp("/")
|
624
653
|
end
|
625
654
|
|
626
|
-
def generate_csrf_token
|
655
|
+
def generate_csrf_token
|
627
656
|
SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH)
|
628
657
|
end
|
629
658
|
|
630
|
-
def encode_csrf_token(csrf_token)
|
659
|
+
def encode_csrf_token(csrf_token)
|
631
660
|
Base64.urlsafe_encode64(csrf_token, padding: false)
|
632
661
|
end
|
633
662
|
|
634
|
-
def decode_csrf_token(encoded_csrf_token)
|
663
|
+
def decode_csrf_token(encoded_csrf_token)
|
635
664
|
Base64.urlsafe_decode64(encoded_csrf_token)
|
636
665
|
end
|
637
666
|
end
|
@@ -1,21 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionController # :nodoc:
|
4
|
-
#
|
6
|
+
# # Action Controller Rescue
|
5
7
|
#
|
6
8
|
# This module is responsible for providing
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
9
|
+
# [rescue_from](rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from) to
|
10
|
+
# controllers, wrapping actions to handle configured errors, and configuring
|
11
|
+
# when detailed exceptions must be shown.
|
10
12
|
module Rescue
|
11
13
|
extend ActiveSupport::Concern
|
12
14
|
include ActiveSupport::Rescuable
|
13
15
|
|
14
|
-
# Override this method if you want to customize when detailed
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
16
|
+
# Override this method if you want to customize when detailed exceptions must be
|
17
|
+
# shown. This method is only called when `consider_all_requests_local` is
|
18
|
+
# `false`. By default, it returns `false`, but someone may set it to
|
19
|
+
# `request.local?` so local requests in production still show the detailed
|
20
|
+
# exception pages.
|
19
21
|
def show_detailed_exceptions?
|
20
22
|
false
|
21
23
|
end
|