Mask even Rails logs properly

Conclusion

For query log masks [this code](https://qiita.com/calorie/items/426fb4c23ab850122f93#activerecordlogsubscriber%E3%81%AB%E3%83%91%E3%83%83%E3%83%81 Patch ʻActiveRecord :: LogSubscriber` with% E3% 82% 92% E3% 81% 82% E3% 81% A6% E3% 82% 8B)

What is a log mask?

It refers to replacing confidential information such as personal information on the log with a specific character string to hide it. Masking is important because it reduces the risk of leaking sensitive information.

Log mask in Rails

When it comes to masking logs in Rails, there is config.filter_parameters. This is a convenient function that masks the parameters on the request log by specifying the parameters you want to mask.

However, as mentioned in this issue, the mask of the query log is not covered. Also, Rails has a log level of debug even in production by default, so Even in production, the query log will flow and confidential information will be exposed.

Rails 6 has released filter_attribute to prevent the issue of being inspected and exposing sensitive information in the log. However, I couldn't mask the query log here either. (It may be possible to mask depending on how Active Record is implemented)

solution

Here are some solutions for query log masking.

Do not output query log

In the first place, setting config.log_level =: info to prevent the query log from being output will solve the problem. However, you may not want to use this method if possible because you will not be able to see all the query logs.

use blouson

There is a gem called blouson, which is a great gem with the ability to mask the query log for a particular table. However, the prefix of the name of the table you want to mask must be secure_, and it will mask the entire query log that is caught in the condition. Therefore, it is a little difficult to use in my environment, and I wanted to mask only specific columns like filter_parameters.

Patch ActiveRecord :: LogSubscriber

I think this is the closest solution to the behavior of filter_attribute.

As shown below, in the part that outputs the log of ʻActiveRecord :: LogSubscriber`, For query logs that bind columns with a specific string, try to mask the bound value.

For MySQL

Add mask processing before render_bind by the method presented in here.

config/initializers/sql_log_filter.rb


module SqlLogFilter
  FILTERS = Set.new(%w(email password))

  def render_bind(attr, value)
    value = '[FILTERED]' if FILTERS.include?(attr.name)
    super(attr, value)
  end
end

ActiveRecord::LogSubscriber.prepend SqlLogFilter

For SQL Server (activerecord-sqlserver-adapter)

Since the parameters are directly embedded in the sql log, the relevant part is omitted with a regular expression and replaced.

config/initializers/sql_log_filter.rb


module SqlLogFilter
  FILTER_REGEX = /email|password/i

  def sql(event)
    binds = event.payload[:binds]
    return super(event) if binds.blank?

    binds.each_with_index do |bind, index|
      next unless bind.name.match?(FILTER_REGEX)

      event.payload[:sql] = event.payload[:sql].gsub(/@#{index} = (N?'(.*?)'|\d+)/) do |_|
        "@#{index} = [FILTERED]"
      end
    end

    super(event)
  end
end

ActiveRecord::LogSubscriber.prepend SqlLogFilter

Have a good mask life!

Recommended Posts

Mask even Rails logs properly