Revert "Revert "Merge pull request #42843 from buckley-w-david/message-verifier-default-serializer""
This reverts commit fd4e63cc28
.
This commit is contained in:
parent
fd4e63cc28
commit
ea9f0103fd
|
@ -42,11 +42,13 @@ class ActiveStorage::DiskController < ActiveStorage::BaseController
|
|||
end
|
||||
|
||||
def decode_verified_key
|
||||
ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)
|
||||
key = ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)
|
||||
key&.deep_symbolize_keys
|
||||
end
|
||||
|
||||
def decode_verified_token
|
||||
ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)
|
||||
token = ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)
|
||||
token&.deep_symbolize_keys
|
||||
end
|
||||
|
||||
def acceptable_content?(token)
|
||||
|
|
|
@ -72,7 +72,7 @@ module ActiveSupport
|
|||
#
|
||||
# === Alternative serializers
|
||||
#
|
||||
# By default MessageVerifier uses Marshal to serialize the message. If you want to use
|
||||
# By default MessageVerifier uses JSON to serialize the message. If you want to use
|
||||
# another serialization method, you can set the serializer in the options
|
||||
# hash upon initialization:
|
||||
#
|
||||
|
@ -115,11 +115,20 @@ module ActiveSupport
|
|||
SEPARATOR = "--" # :nodoc:
|
||||
SEPARATOR_LENGTH = SEPARATOR.length # :nodoc:
|
||||
|
||||
cattr_accessor :default_message_verifier_serializer, instance_accessor: false, default: :marshal
|
||||
|
||||
def initialize(secret, digest: nil, serializer: nil)
|
||||
raise ArgumentError, "Secret should not be nil." unless secret
|
||||
@secret = secret
|
||||
@digest = digest&.to_s || "SHA1"
|
||||
@serializer = serializer || Marshal
|
||||
@serializer = serializer ||
|
||||
if @@default_message_verifier_serializer.equal?(:marshal)
|
||||
Marshal
|
||||
elsif @@default_message_verifier_serializer.equal?(:hybrid)
|
||||
JsonWithMarshalFallback
|
||||
elsif @@default_message_verifier_serializer.equal?(:json)
|
||||
JSON
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if a signed message could have been generated by signing an object
|
||||
|
|
|
@ -167,6 +167,15 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
initializer "active_support.set_default_message_verifier_serializer" do |app|
|
||||
config.after_initialize do
|
||||
unless app.config.active_support.default_message_verifier_serializer.nil?
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer =
|
||||
app.config.active_support.default_message_verifier_serializer
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initializer "active_support.set_marshal_serialization" do |app|
|
||||
config.after_initialize do
|
||||
unless app.config.active_support.use_marshal_serialization.nil?
|
||||
|
|
|
@ -19,7 +19,7 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
|
||||
def setup
|
||||
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
|
||||
@data = { some: "data", now: Time.utc(2010) }
|
||||
@data = { "some" => "data", "now" => Time.utc(2010) }
|
||||
@secret = SecureRandom.random_bytes(32)
|
||||
end
|
||||
|
||||
|
@ -70,25 +70,6 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
ActiveSupport.parse_json_times, Time.zone = previous
|
||||
end
|
||||
|
||||
def test_raise_error_when_argument_class_is_not_loaded
|
||||
# To generate the valid message below:
|
||||
#
|
||||
# AutoloadClass = Struct.new(:foo)
|
||||
# valid_message = @verifier.generate(foo: AutoloadClass.new('foo'))
|
||||
#
|
||||
valid_message = "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA==--f3ef39a5241c365083770566dc7a9eb5d6ace914"
|
||||
exception = assert_raise(ArgumentError, NameError) do
|
||||
@verifier.verified(valid_message)
|
||||
end
|
||||
assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass",
|
||||
"undefined class/module MessageVerifierTest::AutoloadClass"], exception.message
|
||||
exception = assert_raise(ArgumentError, NameError) do
|
||||
@verifier.verify(valid_message)
|
||||
end
|
||||
assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass",
|
||||
"undefined class/module MessageVerifierTest::AutoloadClass"], exception.message
|
||||
end
|
||||
|
||||
def test_raise_error_when_secret_is_nil
|
||||
exception = assert_raise(ArgumentError) do
|
||||
ActiveSupport::MessageVerifier.new(nil)
|
||||
|
@ -96,12 +77,6 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
assert_equal "Secret should not be nil.", exception.message
|
||||
end
|
||||
|
||||
def test_backward_compatibility_messages_signed_without_metadata
|
||||
signed_message = "BAh7BzoJc29tZUkiCWRhdGEGOgZFVDoIbm93SXU6CVRpbWUNIIAbgAAAAAAHOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7BkY=--d03c52c91dfe4ccc5159417c660461bcce005e96"
|
||||
assert_equal @data, @verifier.verify(signed_message)
|
||||
end
|
||||
|
||||
|
||||
def test_rotating_secret
|
||||
old_message = ActiveSupport::MessageVerifier.new("old", digest: "SHA1").generate("old")
|
||||
|
||||
|
@ -124,6 +99,35 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
assert_equal "older", verifier.verified(older_message)
|
||||
end
|
||||
|
||||
def test_rotations_with_metadata
|
||||
old_message = ActiveSupport::MessageVerifier.new("old").generate("old", purpose: :rotation)
|
||||
|
||||
verifier = ActiveSupport::MessageVerifier.new(@secret)
|
||||
verifier.rotate "old"
|
||||
|
||||
assert_equal "old", verifier.verified(old_message, purpose: :rotation)
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultMarshalSerializerMessageVerifierTest < MessageVerifierTest
|
||||
def setup
|
||||
@default_verifier = ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = :marshal
|
||||
|
||||
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
|
||||
@data = { some: "data", now: Time.utc(2010) }
|
||||
@secret = SecureRandom.random_bytes(32)
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = @default_verifier
|
||||
end
|
||||
|
||||
def test_backward_compatibility_messages_signed_without_metadata
|
||||
signed_message = "BAh7BzoJc29tZUkiCWRhdGEGOgZFVDoIbm93SXU6CVRpbWUNIIAbgAAAAAAHOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7BkY=--d03c52c91dfe4ccc5159417c660461bcce005e96"
|
||||
assert_equal @data, @verifier.verify(signed_message)
|
||||
end
|
||||
|
||||
def test_on_rotation_is_called_and_verified_returns_message
|
||||
older_message = ActiveSupport::MessageVerifier.new("older", digest: "SHA1").generate({ encoded: "message" })
|
||||
|
||||
|
@ -138,13 +142,127 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
assert rotated
|
||||
end
|
||||
|
||||
def test_rotations_with_metadata
|
||||
old_message = ActiveSupport::MessageVerifier.new("old").generate("old", purpose: :rotation)
|
||||
def test_raise_error_when_argument_class_is_not_loaded
|
||||
# To generate the valid message below:
|
||||
#
|
||||
# AutoloadClass = Struct.new(:foo)
|
||||
# valid_message = @verifier.generate(foo: AutoloadClass.new('foo'))
|
||||
#
|
||||
valid_message = "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA==--f3ef39a5241c365083770566dc7a9eb5d6ace914"
|
||||
exception = assert_raise(ArgumentError, NameError) do
|
||||
@verifier.verified(valid_message)
|
||||
end
|
||||
assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass",
|
||||
"undefined class/module MessageVerifierTest::AutoloadClass"], exception.message
|
||||
exception = assert_raise(ArgumentError, NameError) do
|
||||
@verifier.verify(valid_message)
|
||||
end
|
||||
assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass",
|
||||
"undefined class/module MessageVerifierTest::AutoloadClass"], exception.message
|
||||
end
|
||||
end
|
||||
|
||||
verifier = ActiveSupport::MessageVerifier.new(@secret)
|
||||
verifier.rotate "old"
|
||||
class MarshalSerializeAndFallbackMessageVerifierTest < DefaultMarshalSerializerMessageVerifierTest
|
||||
def setup
|
||||
@default_verifier = ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
@default_use_marshal = ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization
|
||||
@default_fallback = ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = :hybrid
|
||||
ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization = true
|
||||
ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization = true
|
||||
|
||||
assert_equal "old", verifier.verified(old_message, purpose: :rotation)
|
||||
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
|
||||
@data = { some: "data", now: Time.utc(2010) }
|
||||
@secret = SecureRandom.random_bytes(32)
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = @default_verifier
|
||||
ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization = @default_use_marshal
|
||||
ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization = @default_fallback
|
||||
end
|
||||
end
|
||||
|
||||
class JsonSerializeMarshalFallbackMessageVerifierTest < MessageVerifierTest
|
||||
def setup
|
||||
@default_verifier = ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
@default_use_marshal = ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization
|
||||
@default_fallback = ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = :hybrid
|
||||
ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization = false
|
||||
ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization = true
|
||||
|
||||
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
|
||||
@data = { "some" => "data", "now" => Time.utc(2010) }
|
||||
@secret = SecureRandom.random_bytes(32)
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = @default_verifier
|
||||
ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization = @default_use_marshal
|
||||
ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization = @default_fallback
|
||||
end
|
||||
|
||||
def test_on_rotation_is_called_and_verified_returns_message
|
||||
older_message = ActiveSupport::MessageVerifier.new("older", digest: "SHA1").generate({ encoded: "message" })
|
||||
|
||||
verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512")
|
||||
verifier.rotate "old", digest: "SHA256"
|
||||
verifier.rotate "older", digest: "SHA1"
|
||||
|
||||
rotated = false
|
||||
message = verifier.verified(older_message, on_rotation: proc { rotated = true })
|
||||
|
||||
assert_equal({ "encoded" => "message" }, message)
|
||||
assert rotated
|
||||
end
|
||||
|
||||
def test_backward_compatibility_messages_signed_marshal_serialized
|
||||
marshal_serialized_signed_message = "BAh7B0kiCXNvbWUGOgZFVEkiCWRhdGEGOwBUSSIIbm93BjsAVEl1OglUaW1lDSCAG8AAAAAABjoJem9uZUkiCFVUQwY7AEY=--ae7480422168507f4a8aec6b1d68bfdfd5c6ef48"
|
||||
assert_equal @data, @verifier.verify(marshal_serialized_signed_message)
|
||||
end
|
||||
end
|
||||
|
||||
class JsonSerializeAndNoFallbackMessageVerifierTest < JsonSerializeMarshalFallbackMessageVerifierTest
|
||||
def setup
|
||||
@default_verifier = ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
@default_use_marshal = ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization
|
||||
@default_fallback = ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = :hybrid
|
||||
ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization = false
|
||||
ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization = false
|
||||
|
||||
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
|
||||
@data = { "some" => "data", "now" => Time.utc(2010) }
|
||||
@secret = SecureRandom.random_bytes(32)
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = @default_verifier
|
||||
ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization = @default_use_marshal
|
||||
ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization = @default_fallback
|
||||
end
|
||||
|
||||
def test_backward_compatibility_messages_signed_marshal_serialized
|
||||
marshal_serialized_signed_message = "BAh7B0kiCXNvbWUGOgZFVEkiCWRhdGEGOwBUSSIIbm93BjsAVEl1OglUaW1lDSCAG8AAAAAABjoJem9uZUkiCFVUQwY7AEY=--ae7480422168507f4a8aec6b1d68bfdfd5c6ef48"
|
||||
assert_raise(JSON::ParserError) do
|
||||
@verifier.verify(marshal_serialized_signed_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultJsonSerializerMessageVerifierTest < JsonSerializeAndNoFallbackMessageVerifierTest
|
||||
def setup
|
||||
@default_verifier = ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = :json
|
||||
|
||||
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
|
||||
@data = { "some" => "data", "now" => Time.utc(2010) }
|
||||
@secret = SecureRandom.random_bytes(32)
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::MessageVerifier.default_message_verifier_serializer = @default_verifier
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -199,7 +317,22 @@ class MessageVerifierMetadataMarshalTest < MessageVerifierMetadataTest
|
|||
end
|
||||
end
|
||||
|
||||
class MessageVerifierMetadataJSONTest < MessageVerifierMetadataTest
|
||||
class MessageVerifierMetadataJsonWithMarshalFallbackTest < MessageVerifierMetadataTest
|
||||
private
|
||||
def verifier_options
|
||||
{ serializer: ActiveSupport::JsonWithMarshalFallback }
|
||||
end
|
||||
end
|
||||
|
||||
class MessageVerifierMetadataJsonTest < MessageVerifierMetadataTest
|
||||
private
|
||||
def verifier_options
|
||||
{ serializer: JSON }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class MessageVerifierMetadataCustomJSONTest < MessageVerifierMetadataTest
|
||||
private
|
||||
def verifier_options
|
||||
{ serializer: MessageVerifierTest::JSONSerializer.new }
|
||||
|
|
|
@ -63,6 +63,7 @@ Below are the default values associated with each target version. In cases of co
|
|||
- [`config.action_dispatch.default_headers`](#config-action-dispatch-default-headers): `{ "X-Frame-Options" => "SAMEORIGIN", "X-XSS-Protection" => "0", "X-Content-Type-Options" => "nosniff", "X-Permitted-Cross-Domain-Policies" => "none", "Referrer-Policy" => "strict-origin-when-cross-origin" }`
|
||||
- [`config.add_autoload_paths_to_load_path`](#config-add-autoload-paths-to-load-path): `false`
|
||||
- [`config.active_support.default_message_encryptor_serializer`](#config-active-support-default-message-encryptor-serializer): `:json`
|
||||
- [`config.active_support.default_message_verifier_serializer`](#config-active-support-default-message-verifier-serializer): `:json`
|
||||
|
||||
#### Default Values for Target Version 7.0
|
||||
|
||||
|
@ -1909,6 +1910,19 @@ Used to help migrate apps from `Marshal` to `JSON` as the default serializer for
|
|||
|
||||
Defaults to `true`.
|
||||
|
||||
#### `config.active_support.default_message_verifier_serializer`
|
||||
|
||||
Specifies what serializer the `MessageVerifier` class will use by default.
|
||||
|
||||
Options are `:json`, `:hybrid`, and `:marshal`. `:hybrid` uses the `JsonWithMarshalFallback` class.
|
||||
|
||||
The default value depends on the `config.load_defaults` target version:
|
||||
|
||||
| Starting with version | The default value is |
|
||||
| --------------------- | -------------------- |
|
||||
| (original) | `:marshal` |
|
||||
| 7.1 | `:json` |
|
||||
|
||||
### Configuring Active Job
|
||||
|
||||
`config.active_job` provides the following configuration options:
|
||||
|
|
|
@ -169,6 +169,85 @@ Alternatively, you could load defaults for 7.1
|
|||
config.load_defaults 7.1
|
||||
```
|
||||
|
||||
### New `ActiveSupport::MessageVerifier` default serializer
|
||||
|
||||
As of Rails 7.1, the default serializer in use by the `MessageVerifier` is `JSON`.
|
||||
This offers a more secure alternative to the current default serializer.
|
||||
|
||||
The `MessageVerifier` offers the ability to migrate the default serializer from `Marshal` to `JSON`.
|
||||
|
||||
If you would like to ignore this change in existing applications, set the following: `config.active_support.default_message_verifier_serializer = :marshal`.
|
||||
|
||||
In order to roll out the new default when upgrading from `7.0` to `7.1`, there are three configuration variables to keep in mind.
|
||||
```
|
||||
config.active_support.default_verifier_serializer
|
||||
config.active_support.fallback_to_marshal_deserialization
|
||||
config.active_support.use_marshal_serialization
|
||||
```
|
||||
|
||||
`default_message_verifier_serializer` defaults to `:json` as of `7.1` but it offers both a `:hybrid` and `:marshal` option.
|
||||
|
||||
In order to migrate an older deployment to `:json`, first ensure that the `default_message_verifier_serializer` is set to `:marshal`.
|
||||
```ruby
|
||||
# config/application.rb
|
||||
config.load_defaults 7.0
|
||||
config.active_support.default_message_verifier_serializer = :marshal
|
||||
```
|
||||
|
||||
Once this is deployed on all Rails processes, set `default_message_verifier_serializer` to `:hybrid` to begin using the
|
||||
`ActiveSupport::JsonWithMarshalFallback` class as the serializer. The defaults for this class are to use `Marshal`
|
||||
as the serializer and to allow the deserialisation of both `Marshal` and `JSON` serialized payloads.
|
||||
|
||||
```ruby
|
||||
config.load_defaults 7.0
|
||||
config.active_support.default_message_verifier_serializer = :hybrid
|
||||
```
|
||||
|
||||
Once this is deployed on all Rails processes, set the following configuration options in order to stop the
|
||||
`ActiveSupport::JsonWithMarshalFallback` class from using `Marshal` to serialize new payloads.
|
||||
|
||||
```ruby
|
||||
# config/application.rb
|
||||
config.load_defaults 7.0
|
||||
config.active_support.default_message_verifier_serializer = :hybrid
|
||||
config.active_support.use_marshal_serialization = false
|
||||
```
|
||||
|
||||
Allow this configuration to run on all processes for a considerable amount of time.
|
||||
`ActiveSupport::JsonWithMarshalFallback` logs the following each time the `Marshal` fallback
|
||||
is used:
|
||||
```
|
||||
JsonWithMarshalFallback: Marshal load fallback occurred.
|
||||
```
|
||||
|
||||
Once those message stop appearing in your logs and you're confident that all `MessageVerifier`
|
||||
payloads in transit are `JSON` serialized, the following configuration options will disable the
|
||||
Marshal fallback in `ActiveSupport::JsonWithMarshalFallback`.
|
||||
|
||||
```ruby
|
||||
# config/application.rb
|
||||
config.load_defaults 7.0
|
||||
config.active_support.default_message_verifier_serializer = :hybrid
|
||||
config.active_support.use_marshal_serialization = false
|
||||
config.active_support.fallback_to_marshal_deserialization = false
|
||||
```
|
||||
|
||||
If all goes well, you should now be safe to migrate the Message Verifier from
|
||||
`ActiveSupport::JsonWithMarshalFallback` to `ActiveSupport::JSON`.
|
||||
To do so, simply swap the `:hybrid` serializer for the `:json` serializer.
|
||||
|
||||
```ruby
|
||||
# config/application.rb
|
||||
config.load_defaults 7.0
|
||||
config.active_support.default_message_verifier_serializer = :json
|
||||
```
|
||||
|
||||
Alternatively, you could load defaults for 7.1
|
||||
```ruby
|
||||
# config/application.rb
|
||||
config.load_defaults 7.1
|
||||
```
|
||||
|
||||
Upgrading from Rails 6.1 to Rails 7.0
|
||||
-------------------------------------
|
||||
|
||||
|
|
|
@ -270,6 +270,7 @@ module Rails
|
|||
|
||||
if respond_to?(:active_support)
|
||||
active_support.default_message_encryptor_serializer = :json
|
||||
active_support.default_message_verifier_serializer = :json
|
||||
end
|
||||
else
|
||||
raise "Unknown version #{target_version.to_s.inspect}"
|
||||
|
|
|
@ -3189,6 +3189,14 @@ module ApplicationTests
|
|||
assert_equal true, ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization
|
||||
end
|
||||
|
||||
test "ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization is true by default for upgraded apps" do
|
||||
remove_from_config '.*config\.load_defaults.*\n'
|
||||
|
||||
app "development"
|
||||
|
||||
assert_equal true, ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization
|
||||
end
|
||||
|
||||
test "ActiveSupport::JsonWithMarshalFallback.fallback_to_marshal_deserialization can be configured via config.active_support.fallback_to_marshal_deserialization" do
|
||||
remove_from_config '.*config\.load_defaults.*\n'
|
||||
|
||||
|
@ -3207,6 +3215,14 @@ module ApplicationTests
|
|||
assert_equal true, ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization
|
||||
end
|
||||
|
||||
test "ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization is true by default for upgraded apps" do
|
||||
remove_from_config '.*config\.load_defaults.*\n'
|
||||
|
||||
app "development"
|
||||
|
||||
assert_equal true, ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization
|
||||
end
|
||||
|
||||
test "ActiveSupport::JsonWithMarshalFallback.use_marshal_serialization can be configured via config.active_support.use_marshal_serialization" do
|
||||
remove_from_config '.*config\.load_defaults.*\n'
|
||||
|
||||
|
@ -3246,6 +3262,32 @@ module ApplicationTests
|
|||
assert_equal :hybrid, ActiveSupport::MessageEncryptor.default_message_encryptor_serializer
|
||||
end
|
||||
|
||||
test "ActiveSupport::MessageVerifier.default_message_verifier_serializer is :json by default for new apps" do
|
||||
app "development"
|
||||
|
||||
assert_equal :json, ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
end
|
||||
|
||||
test "ActiveSupport::MessageVerifier.default_message_verifier_serializer is :marshal by default for upgraded apps" do
|
||||
remove_from_config '.*config\.load_defaults.*\n'
|
||||
|
||||
app "development"
|
||||
|
||||
assert_equal :marshal, ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
end
|
||||
|
||||
test "ActiveSupport::MessageVerifier.default_message_verifier_serializer can be configured via config.active_support.default_message_verifier_serializer" do
|
||||
remove_from_config '.*config\.load_defaults.*\n'
|
||||
|
||||
app_file "config/initializers/default_message_verifier_serializer.rb", <<-RUBY
|
||||
Rails.application.config.active_support.default_message_verifier_serializer = :hybrid
|
||||
RUBY
|
||||
|
||||
app "development"
|
||||
|
||||
assert_equal :hybrid, ActiveSupport::MessageVerifier.default_message_verifier_serializer
|
||||
end
|
||||
|
||||
test "unknown_asset_fallback is false by default" do
|
||||
app "development"
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ module ApplicationTests
|
|||
assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie
|
||||
end
|
||||
|
||||
test "signed cookies with SHA512 digest and rotated out SHA256 and SHA1 digests" do
|
||||
test "signed cookies with SHA512 digest and marshal serializer and rotated out SHA256 and SHA1 digests" do
|
||||
app_file "config/routes.rb", <<-RUBY
|
||||
Rails.application.routes.draw do
|
||||
get ':controller(/:action)'
|
||||
|
@ -88,8 +88,8 @@ module ApplicationTests
|
|||
sha256_secret = Rails.application.key_generator.generate_key("sha256")
|
||||
|
||||
::TestVerifiers = Class.new do
|
||||
class_attribute :sha1, default: ActiveSupport::MessageVerifier.new(sha1_secret, digest: "SHA1")
|
||||
class_attribute :sha256, default: ActiveSupport::MessageVerifier.new(sha256_secret, digest: "SHA256")
|
||||
class_attribute :sha1, default: ActiveSupport::MessageVerifier.new(sha1_secret, digest: "SHA1", serializer: Marshal)
|
||||
class_attribute :sha256, default: ActiveSupport::MessageVerifier.new(sha256_secret, digest: "SHA256", serializer: Marshal)
|
||||
end
|
||||
|
||||
config.action_dispatch.signed_cookie_digest = "SHA512"
|
||||
|
@ -104,7 +104,77 @@ module ApplicationTests
|
|||
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
verifier_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512)
|
||||
verifier_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512, serializer: Marshal)
|
||||
|
||||
get "/foo/write_raw_cookie_sha1"
|
||||
get "/foo/read_signed"
|
||||
assert_equal "signed cookie".inspect, last_response.body
|
||||
|
||||
get "/foo/read_raw_cookie"
|
||||
assert_equal "signed cookie", verifier_sha512.verify(last_response.body, purpose: "cookie.signed_cookie")
|
||||
|
||||
get "/foo/write_raw_cookie_sha256"
|
||||
get "/foo/read_signed"
|
||||
assert_equal "signed cookie".inspect, last_response.body
|
||||
|
||||
get "/foo/read_raw_cookie"
|
||||
assert_equal "signed cookie", verifier_sha512.verify(last_response.body, purpose: "cookie.signed_cookie")
|
||||
end
|
||||
|
||||
test "signed cookies with SHA512 digest and json serializer and rotated out SHA256 and SHA1 digests" do
|
||||
app_file "config/routes.rb", <<-RUBY
|
||||
Rails.application.routes.draw do
|
||||
get ':controller(/:action)'
|
||||
post ':controller(/:action)'
|
||||
end
|
||||
RUBY
|
||||
|
||||
controller :foo, <<-RUBY
|
||||
class FooController < ActionController::Base
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
def write_raw_cookie_sha1
|
||||
cookies[:signed_cookie] = TestVerifiers.sha1.generate("signed cookie")
|
||||
head :ok
|
||||
end
|
||||
|
||||
def write_raw_cookie_sha256
|
||||
cookies[:signed_cookie] = TestVerifiers.sha256.generate("signed cookie")
|
||||
head :ok
|
||||
end
|
||||
|
||||
def read_signed
|
||||
render plain: cookies.signed[:signed_cookie].inspect
|
||||
end
|
||||
|
||||
def read_raw_cookie
|
||||
render plain: cookies[:signed_cookie]
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
add_to_config <<-RUBY
|
||||
sha1_secret = Rails.application.key_generator.generate_key("sha1")
|
||||
sha256_secret = Rails.application.key_generator.generate_key("sha256")
|
||||
|
||||
::TestVerifiers = Class.new do
|
||||
class_attribute :sha1, default: ActiveSupport::MessageVerifier.new(sha1_secret, digest: "SHA1", serializer: JSON)
|
||||
class_attribute :sha256, default: ActiveSupport::MessageVerifier.new(sha256_secret, digest: "SHA256", serializer: JSON)
|
||||
end
|
||||
|
||||
config.action_dispatch.signed_cookie_digest = "SHA512"
|
||||
config.action_dispatch.signed_cookie_salt = "sha512 salt"
|
||||
config.action_dispatch.cookies_serializer = :json
|
||||
|
||||
config.action_dispatch.cookies_rotations.tap do |cookies|
|
||||
cookies.rotate :signed, sha1_secret, digest: "SHA1"
|
||||
cookies.rotate :signed, sha256_secret, digest: "SHA256"
|
||||
end
|
||||
RUBY
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
verifier_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512, serializer: JSON)
|
||||
|
||||
get "/foo/write_raw_cookie_sha1"
|
||||
get "/foo/read_signed"
|
||||
|
|
Loading…
Reference in New Issue