September 3, 2019
This blog is part of our Rails 6 series.
A lot of times, we ask user for sensitive data such as password, credit card number etc. We should not be able to see this information in logs. So, there must be a way in Rails to filter out these parameters from logs.
Rails provides a way of doing this. We can add parameters to Rails.application.config.filter_parameters.
There is one more way of doing this in Rails. We can also use https://api.rubyonrails.org/classes/ActionDispatch/Http/FilterParameters.html.
However there is still a security issue when we call inspect on an ActiveRecord object for logging purposes. In this case, Rails does not consider Rails.application.config.filter_parameters and displays the sensitive information.
Rails 6 fixes this. It considers Rails.application.config.filter_parameters while inspecting an object.
Rails 6 also provides an alternative way to filter columns on ActiveRecord level by adding filter_attributes on ActiveRecord::Base.
In Rails 6, filter_attributes on ActiveRecord::Base takes priority over Rails.application.config.filter_parameters.
Let's checkout how it works.
Let's create a user record and call inspect on it.
>> class User < ApplicationRecord
>> validates :email, :password, presence: true
>> end
=> {:presence=>true}
>> User.create(email: '[email protected]', password: 'john_wick_bigbinary')
BEGIN
User Create (0.6ms) INSERT INTO "users" ("email", "password", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["email", "[email protected]"], ["password", "john_wick_bigbinary"], ["created_at", "2019-05-17 21:34:34.504394"], ["updated_at", "2019-05-17 21:34:34.504394"]]
COMMIT
=> #<User id: 2, email: "[email protected]", password: [FILTERED], created_at: "2019-05-17 21:34:34", updated_at: "2019-05-17 21:34:34">
We can see that password
is filtered as it is added to
Rails.application.config.filter_parameters
by default in config/initializers/filter_parameter_logging.rb
.
Now let's add just :email
to User.filter_attributes
>> User.filter_attributes = [:email]
=> [:email]
>> User.first.inspect
SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> "#<User id: 2, email: [FILTERED], password: \"john_wick_bigbinary\", created_at: \"2019-05-17 21:34:34\", updated_at: \"2019-05-17 21:34:34\">"
We can see here that User.filter_attributes
took priority over
Rails.application.config.filter_parameters
and removed filtering from password and filtered just email.
Now, let's add both :email
and :password
to User.filter_attributes
.
>> User.filter_attributes = [:email, :password]
=> [:email, :password]
>> User.first.inspect
=> "#<User id: 2, email: [FILTERED], password: [FILTERED], created_at: \"2019-05-17 21:34:34\", updated_at: \"2019-05-17 21:34:34\">"
We can see that now both email
and password
are filtered out.
Here is the relevant pull request.
If this blog was helpful, check out our full blog archive.