Change to ActiveRecord deserialization (CVE-2022-32224)

Mimicking the offical change in Rails 5.1.8 to protect against CVE-2022-32224, all versions of Rails LTS try to use YAML.safe_load to deserialize database columns in ActiveRecord. This is a potential breaking change.

Background

When using something like

class MyModel < ActiveRecord::Base
  serialize :address_data
  # or alternatively
  store :settings, accessor: [:color, :homepage]
end

ActiveRecord will use YAML to serialize and deserialize data. However, YAML deserialization using YAML.load (or explicitly YAML.unsafe_load in newer YAML versions) is dangerous when the persisted data contains malicious content leading to potential Remote Code Execution.

There is no regular known way for an attacker to get this malicious content into the serialized column, however if an attacker manages to get write access to the database through another security issue (like some SQL interpolation bug), they could elevate this attack to a Remote Code Execution, by putting such malicious content into a serialized database column.

Because of this, Rails LTS tries to use YAML.safe_load where possible, which will only allow deserialization of certain permitted classes. These classes are by default

Symbol
Date
Time
DateTime
ActiveSupport::HashWithIndifferentAccess
ActionDispatch::Http::ParamsHashWithIndifferentAccess
ActionController::Parameters

Note on Ruby 1.8.7 / YAML < 2

If you use Ruby 1.8.7, or you use Ruby < 2.1 and have not added psych version 2+ to your Gemfile, YAML.safe_load is not available, and Rails LTS will not be able to protect you against this attack. As noted, this is only a way to escalate another security issue, not a problem in and of itself.

Breaking change

If your code does serialize Ruby classes that are not permitted, reading these classes will cause an exception. You will have to either

  • disable the feature altogether, by setting config.active_record.use_yaml_unsafe_load = true
  • or adding the relevant classes using config.active_record.yaml_column_permitted_classes += ['MyClass']

Do not add classes that might eval some string upon initialization.

If you are unsure which classes you need to permit, you can either manually check the content of your database columns, or you can temporarily disable the feature and add some in-production tracking Show archive.org snapshot for a while.

Tobias Kraze 3 months ago