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

  1. Identify Sensitive Fields: Make a list of all sensitive fields in your GraphQL schema
  2. Test Your Filtering: Ensure sensitive data is properly redacted in logs
  3. Document Your Approach: Make sure your team knows which fields are redacted
  4. 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.

How to redact sensitive parameters in GraphQL and Rails? - Abhay Nikam