Skip to content

Commit ee81e4a

Browse files
Rails: Filter sensitive information from external network requests
This was lifted from an existing [blog post](https://thoughtbot.com/blog/parameter-filtering).
1 parent 4dac788 commit ee81e4a

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

rails/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Guidance on ActiveRecord, ActiveModel, and other model objects.
8484
## Security
8585

8686
- Set [config.sandbox_by_default][sandbox] to `true` in production-like environments to avoid accidental writing to the production database.
87+
- Filter sensitive information from external network requests. [Example](/rails/how-to/filter-sensitive-information-from-external-network-requests.md).
8788

8889
[sandbox]: https://guides.rubyonrails.org/configuring.html#config-sandbox-by-default
8990

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Filter sensitive information from external network requests
2+
3+
If you’re using Faraday, it’s your responsibility to [filter sensitive
4+
information][1] when logging requests. This does not happen by default.
5+
6+
```ruby
7+
conn = Faraday.new(url: "http://httpbingo.org") do |builder|
8+
builder.response :logger do | logger |
9+
logger.filter(/(api_key=)([^&]+)/, '\1[REMOVED]')
10+
end
11+
end
12+
```
13+
14+
However, since Rails provides an [API for filtering parameters][2], we can
15+
create a [custom formatter][3] to re-use that configuration.
16+
17+
```ruby
18+
conn = Faraday.new(url: "http://httpbingo.org") do |builder|
19+
builder.response :logger, nil, {
20+
formatter: ApplicationFormatter
21+
}
22+
end
23+
24+
class ApplicationFormatter < Faraday::Logging::Formatter
25+
def request(env)
26+
info("Request") { log_url(env.url) }
27+
info("Request") { log_body(env.body) } if env.body && log_body?
28+
end
29+
30+
def response(env)
31+
info("Response") { log_url(env.url) }
32+
info("Response") { log_body(env.body) } if env.body && log_body?
33+
end
34+
35+
private
36+
37+
# Re-uses existing configuration from config/initializers/filter_parameter_logging.rb
38+
def filter_parameters
39+
@filter_parameters ||= Rails.configuration.filter_parameters
40+
end
41+
42+
# Filters parameters
43+
def parameter_filter(**options)
44+
ActiveSupport::ParameterFilter.new(filter_parameters, **options)
45+
end
46+
47+
def parse_json(json)
48+
JSON.parse(json, object_class: HashWithIndifferentAccess)
49+
end
50+
51+
def log_body?
52+
@options[:bodies]
53+
end
54+
55+
def log_body(body)
56+
result = walk(body)
57+
58+
parameter_filter.filter(result).pretty_inspect
59+
end
60+
61+
def log_url(url)
62+
filtered_url = filter_url(url)
63+
64+
filtered_url.to_s
65+
end
66+
67+
def filter_url(url)
68+
return url if url.query.nil?
69+
70+
params = URI.decode_www_form(url.query).to_h
71+
filtered_params = parameter_filter(mask: "FILTERED").filter(params)
72+
url.query = URI.encode_www_form(filtered_params)
73+
end
74+
75+
def walk(obj)
76+
case obj
77+
when Hash
78+
obj.transform_values { walk(_1) }
79+
when Array
80+
obj.map { walk(_1) }
81+
when String
82+
parse_json(obj)
83+
else
84+
obj
85+
end
86+
rescue JSON::ParserError
87+
obj
88+
end
89+
end
90+
```
91+
92+
[1]: https://lostisland.github.io/faraday/#/middleware/included/logging?id=filter-sensitive-information
93+
[2]: https://guides.rubyonrails.org/action_controller_advanced_topics.html#parameters-filtering
94+
[3]: https://lostisland.github.io/faraday/#/middleware/included/logging?id=customize-the-formatter

0 commit comments

Comments
 (0)