Technical information leak - API - Ruby

Technical information leak - API - Ruby

Need

Protection of sensitive technical information in API responses

Context

  • Usage of Ruby 2.7 for developing Ruby applications
  • Usage of Rails framework for building web applications
  • Usage of GraphQL for querying and manipulating data

Description

Non compliant code

        # app/controllers/graphql_controller.rb
class GraphqlController < ApplicationController
  def execute
    result = MySchema.execute(
      params[:query],
      operation_name: params[:operationName],
      context: context,
      variables: ensure_hash(params[:variables])
    )
    render json: result
  end

  private

  # Handle form data, JSON body, or a blank value
  def ensure_hash(ambiguous_param)
    case ambiguous_param
    when String
      if ambiguous_param.present?
        ensure_hash(JSON.parse(ambiguous_param))
      else
        {}
      end
    when Hash, ActionController::Parameters
      ambiguous_param
    when nil
      {}
    else
      raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
    end
  end
end
        
        

The above code is a typical controller for handling GraphQL requests in a Ruby on Rails application. The execute method takes in parameters from the request, including the query, operation name, context, and variables, and then executes the query against the GraphQL schema.

The vulnerability lies in the fact that the entire GraphQL schema is exposed to the client, including all queries and mutations. An attacker could craft a special query to introspect the schema and retrieve all types, fields, arguments, directives, etc. This could potentially leak sensitive information about underlying data structures and business logic.

The ensure_hash method is used to parse the variables parameter, which could be a JSON string, a hash, or form data. However, it does not perform any kind of authorization or validation on the incoming query, which means an attacker could send any query or mutation they want. This could lead to unauthorized data access or manipulation.

Furthermore, the render json: result line directly outputs the result of the query execution to the client without any filtering or sanitization. This could potentially expose sensitive data to the client, depending on what the query returns.

Steps

  • Implement authentication and authorization mechanisms to ensure that only authorized users can access the GraphQL API.
  • Restrict access to the GraphQL schema endpoint by implementing proper access controls, such as role-based access control or API key authentication.
  • Remove the ability to execute arbitrary queries and mutations from the GraphQL schema. Define a whitelist of allowed queries and mutations that can be executed.
  • Implement rate limiting to prevent abuse and excessive requests to the GraphQL API.
  • Regularly review and update the GraphQL schema to remove any sensitive or unnecessary information.
  • Implement input validation and sanitization to prevent injection attacks and ensure that only valid and expected data is accepted by the GraphQL API.
  • Implement logging and monitoring to detect any suspicious or unauthorized access attempts to the GraphQL API.
  • Keep the GraphQL server and its dependencies up to date with the latest security patches and updates.
  • Conduct regular security audits and penetration testing to identify and fix any potential vulnerabilities in the GraphQL API.

Compliant code

        # app/controllers/graphql_controller.rb
class GraphqlController < ApplicationController
  before_action :authenticate_user!
  before_action :authorize_user!

  def execute
    result = MySchema.execute(
      params[:query],
      operation_name: params[:operationName],
      context: context,
      variables: ensure_hash(params[:variables])
    )
    render json: result
  end

  private

  def authenticate_user!
    # Implement your authentication logic here
  end

  def authorize_user!
    # Implement your authorization logic here
  end

  # Handle form data, JSON body, or a blank value
  def ensure_hash(ambiguous_param)
    case ambiguous_param
    when String
      if ambiguous_param.present?
        ensure_hash(JSON.parse(ambiguous_param))
      else
        {}
      end
    when Hash, ActionController::Parameters
      ambiguous_param
    when nil
      {}
    else
      raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
    end
  end
end
        
        

The updated code includes two new before_action callbacks: authenticate_user! and authorize_user!. These methods should contain the logic for authenticating and authorizing users, respectively.

The authenticate_user! method should verify the user's identity, typically by checking a session cookie or token. If the user is not authenticated, the method should halt the request and return an error response.

The authorize_user! method should check if the authenticated user has the necessary permissions to access the requested resource. If the user is not authorized, the method should halt the request and return an error response.

These changes ensure that only authenticated and authorized users can access the GraphQL API, which helps to prevent unauthorized information disclosure.

Please note that the actual implementation of authenticate_user! and authorize_user! will depend on your specific authentication and authorization setup.

In addition, you should also consider implementing rate limiting, input validation and sanitization, logging and monitoring, and regular security audits as part of your overall security strategy.

References