33

How would I go about manually filtering a hash using my application's parameter filter?

I imagine it'd go like this:

Rails.application.filter :password => 'pass1234'
  # => {:password => '[FILTERED]'}

EDIT (clarification): I'm aware that Rails filters the params hash when writing to the logs. What I want to do is apply that same filter to a different hash at my prerogative before writing it to the logs with something like Rails.logger.info. I'm calling a remote HTTP query as a part of my application (since most of the backend operates through a remote API), and I'm logging the URL and parameters passed. I want to have the logs but also ensure that none of the sensitive params show up there.

Steven
  • 17,796
  • 13
  • 66
  • 118

3 Answers3

57

After a few minutes of shotgunning it, I figured out this was the way to do it:

filters = Rails.application.config.filter_parameters
f = ActionDispatch::Http::ParameterFilter.new filters
f.filter :password => 'haha' # => {:password=>"[FILTERED]"}
Steven
  • 17,796
  • 13
  • 66
  • 118
  • Thanks. This was helpful for a sinatra app. – Isaac Betesh Aug 26 '14 at 19:53
  • @IsaacBetesh how to use the filtered value in this code: `session[:user] = params[:user][:name]`? – Sahil Oct 03 '16 at 07:42
  • I don't understand the question, @sahil. Can you give an example? Does it relate to this question? – Isaac Betesh Oct 05 '16 at 18:58
  • @IsaacBetesh, I am generating a password in backend which is supposed to be sent in a post request. Can I make it filtered and send it in a request? for eg, the password generated is in a variable, `some_password = "password"`, now I want to make it filtered and send it in a request. – Sahil Oct 06 '16 at 04:08
  • 1
    @sahil Filtering parameters is to prevent data sent from the client from being logged on the server. Data that the server sends to the client is not logged on the server by default, though if you were to log it, you could follow steven-xu's approach. – Isaac Betesh Oct 06 '16 at 18:02
  • 1
    For Rails 6+, `ActionDispatch::Http::ParameterFilter` has been changed to `ActiveSupport::ParameterFilter`. – mrudult Dec 02 '21 at 12:55
22

See the config/application.rb file, towards the end there is a line:

config.filter_parameters += [:password]

This way the "password" param will not be shown in logs, but you can still access the value normally.

Edit

It seem that have misunderstood your meaning of "filter" originally. As for the clarified issue, I have no idea on how to handle it the truly Rails way.

Here is a brute force approach:

  1. Parse the query with CGI::parse(URI.parse(my_url_address_with_params).query) to get a hash of param/values (note: values are actually stored as an array; here is the discussion).
  2. Locate the parameters you want to filter out and replace values with literal *filtered*.
  3. Call Rails.logger.info (or debug) directly to log.

Here is what you should dig into when relying on Rails magical classes and methods:

In Rails 3 the code that does the trick seems to live in ActionDispatch::Http (ParameterFilter in particular, method `filtered_parameters'). The documentation is available at API Dock (or, to be honest, very little documentation). You can examine the sources to get an idea of how this works.

My knowledge of Rails internals is not good enough to suggest anything else. I believe that someone with a better understanding of it might be of more help.

Community
  • 1
  • 1
Miki
  • 7,052
  • 2
  • 29
  • 39
  • Sorry. Please see my clarification. I'm aware that Rails filters the parameters before logging them, and I know how to add to the filter. What I'd like to do is apply that same filter to another hash and then log that. – Steven May 27 '11 at 15:41
  • Thanks for the answer. It's too bad there doesn't appear a straightforward solution at this point. I don't mind implementing the filter myself. – Steven May 27 '11 at 18:03
  • For the record, I've [sorted it out](http://stackoverflow.com/questions/6152388/manually-filter-parameters-in-rails/6156581#6156581) after fiddling with it for a while. – Steven May 27 '11 at 19:01
  • Great to hear that! To be honest, I had no energy to play with it, but glad that you have found the solution :) – Miki May 27 '11 at 19:19
4

Building on Steven Xu's answer above, I made this initializer in my rails app:

class ActionController::Parameters
  def filtered
    ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters).filter(self)
  end
end

Which let's me call params.filtered

[1] pry(#<LessonsController>)> params.filtered
{
  "controller" => "lessons",
  "action"     => "search",
  "locale"     => "en"
}
[2] pry(#<LessonsController>)> params[:password] = "bob"
"bob"
[3] pry(#<LessonsController>)> params.filtered
{
  "controller" => "lessons",
  "action"     => "search",
  "locale"     => "en",
  "password"   => "[FILTERED]"
}
pixelearth
  • 13,674
  • 10
  • 62
  • 110
  • how to use the filtered value in this code: `session[:user] = params[:user][:name]`. – Sahil Oct 03 '16 at 07:44
  • Here's a gist that does this recursively. https://gist.github.com/hopsoft/79692dcfc7e5a8f82455b85a743f05c1 – Hopsoft Jan 19 '18 at 19:05