How to redact sensitive parameters in GraphQL and Rails?
When building GraphQL APIs with Rails, it's important to protect sensitive information from being logged or exposed. This post covers how to redact sensitive parameters in GraphQL queries.
The Problem
GraphQL queries can contain sensitive information like passwords, API keys, or tokens. By default, Rails may log these parameters, which poses a security risk.
Solution: Parameter Filtering
Rails provides a way to filter sensitive parameters from logs. You can configure this in your config/application.rb:
config.filter_parameters += [:password, :password_confirmation, :token, :api_key]
GraphQL-Specific Approach
For GraphQL queries, you'll need to filter parameters at the GraphQL level. Here's how to do it with graphql-ruby:
# app/graphql/your_schema.rb
class YourSchema < GraphQL::Schema
def self.execute(query_string:, context:, variables: {}, operation_name: nil)
# Redact sensitive variables
redacted_variables = redact_sensitive_data(variables)
super(
query_string: query_string,
context: context,
variables: redacted_variables,
operation_name: operation_name
)
end
private
def self.redact_sensitive_data(variables)
sensitive_keys = %w[password passwordConfirmation token apiKey]
variables.deep_transform_values do |value|
if value.is_a?(Hash)
value.transform_keys do |key|
sensitive_keys.include?(key.to_s) ? "[REDACTED]" : key
end
else
value
end
end
end
end
Logging Middleware
You can also create custom logging middleware to redact sensitive data:
# app/middleware/graphql_logging_middleware.rb
class GraphQLLoggingMiddleware
SENSITIVE_PARAMS = %w[password passwordConfirmation token apiKey].freeze
def initialize(app)
@app = app
end
def call(env)
request = ActionDispatch::Request.new(env)
if graphql_request?(request)
redact_query(request)
end
@app.call(env)
end
private
def graphql_request?(request)
request.path == '/graphql' && request.post?
end
def redact_query(request)
query_params = request.params['query']
variables = request.params['variables']
if variables.present?
redacted_variables = redact_variables(variables)
request.params['variables'] = redacted_variables
end
end
def redact_variables(variables)
return variables unless variables.is_a?(Hash)
variables.transform_values do |value|
if value.is_a?(Hash)
redact_variables(value)
elsif SENSITIVE_PARAMS.any? { |key| variables.key?(key) }
'[REDACTED]'
else
value
end
end
end
end
Best Practices
- Identify Sensitive Fields: Make a list of all sensitive fields in your GraphQL schema
- Test Your Filtering: Ensure sensitive data is properly redacted in logs
- Document Your Approach: Make sure your team knows which fields are redacted
- Regular Audits: Periodically review logs to ensure no sensitive data leaks through
Conclusion
Protecting sensitive data in GraphQL queries is crucial for application security. By implementing proper parameter filtering and logging middleware, you can ensure that sensitive information never appears in your logs.