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.
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
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.
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 andfor a while.