HIGH bola idorgrapebearer tokens

Bola Idor in Grape with Bearer Tokens

Bola Idor in Grape with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Broken Object Level Authorization (BOLA) occurs when an API fails to enforce proper authorization checks between a user and a specific resource identifier. In Grape-based Ruby APIs, this often maps to an endpoint like GET /users/:id where the route parameter :id is treated as an object ID. If the API only checks that a request carries a Bearer Token and a valid JWT or opaque token, but does not verify that the authenticated subject is allowed to access the resource identified by :id, an attacker can change the ID to another user’s record and read or manipulate data.

Bearer Tokens are commonly used in Grape APIs via the Authorization: Bearer <token> header. A token may encode a user ID (sub claim) or roles, but if the endpoint uses a different object ID (e.g., an account ID or a profile ID) and does not re-check ownership, the trust in the token alone becomes the sole gate. For example, a token belonging to user 101 should not permit user 101 to perform GET /accounts/102 unless there is explicit authorization logic confirming that user 101 owns or has rights to account 102. Without such checks, the API exhibits BOLA because the object-level mapping between the subject in the token and the resource ID is missing or incomplete.

Grape endpoints often parse the token in a before filter and store the subject in env for later use. If subsequent resource loading uses only the route parameter without cross-referencing the authenticated subject, BOLA is introduced. Consider a Grape API that decodes a Bearer Token to obtain a user identifier and then loads an associated record by a separate ID provided by the client. If the authorization check is limited to confirming the token is valid and does not assert that the loaded record belongs to the authenticated user, the vulnerability is present. This is especially common in nested resources such as GET /users/:user_id/projects/:project_id, where an attacker can increment or guess :project_id to access projects belonging to other users.

Real-world attack patterns mirror findings from OWASP API Top 10 and can be discovered by scanners that correlate authentication state with object-level authorization tests. middleBrick scans this combination by running authenticated and unauthenticated probes, then comparing behavior when the Bearer Token is swapped across different object IDs. The scanner checks whether endpoints that accept an object identifier properly scope that identifier to the token’s subject, and it reports findings when access is granted without proper ownership or role-based checks. Proper remediation requires explicit mapping between the token’s subject and the requested resource, enforced at the endpoint or in shared authorization logic.

Bearer Tokens-Specific Remediation in Grape — concrete code fixes

To fix BOLA in Grape when using Bearer Tokens, ensure that every object-level operation validates that the authenticated subject has permission to access the requested object. Below are concrete code examples that demonstrate secure patterns.

Example 1: Scoped authorization with explicit ownership check

In this example, a Grape endpoint loads a user profile by ID and verifies that the ID matches the subject in the Bearer Token.

require 'grape'
require 'jwt'

class MyAPI < Grape::API
  helpers do
    def current_user
      @current_user ||= begin
        auth_header = request.env['HTTP_AUTHORIZATION']
        token = auth_header.to_s.split(' ').last if auth_header&.start_with?('Bearer ')
        return nil unless token
        decoded = JWT.decode(token, 'your_secret_key', true, { algorithm: 'HS256' })
        { sub: decoded.first['sub'] } # subject should be user ID
      rescue JWT::DecodeError
        nil
      end
    end

    def authorize_user!(user_id)
      error!('Forbidden', 403) unless current_user&.fetch(:sub) == user_id.to_s
    end
  end

  before { authenticate_bearer! }

  desc 'Get user profile'
  params do
    requires :id, type: Integer, desc: 'User ID'
  end
  get 'users/:id' do
    authorize_user!(params[:id])
    # Safe to proceed: current_user subject matches requested ID
    { user_id: params[:id], name: 'Alice' }
  end
end

# Authentication helper
private
def authenticate_bearer!
  auth_header = request.env['HTTP_AUTHORIZATION']
  halt 401, { error: 'Unauthorized' } unless auth_header&.start_with?('Bearer ')
  token = auth_header.split(' ').last
  begin
    JWT.decode(token, 'your_secret_key', true, { algorithm: 'HS256' })
  rescue JWT::DecodeError
    halt 401, { error: 'Invalid token' }
  end
end

Example 2: Nested resources with proper scoping

For nested routes, validate both parent and child ownership or relationship rather than trusting the token subject alone.

require 'grape'

class MyAPI < Grape::API
  helpers do
    def current_user
      # decode token as shown above
      auth_header = request.env['HTTP_AUTHORIZATION']
      token = auth_header.to_s.split(' ').last if auth_header&.start_with?('Bearer ')
      return nil unless token
      decoded = JWT.decode(token, 'your_secret_key', true, { algorithm: 'HS256' })
      { sub: decoded.first['sub'] }
    rescue
      nil
    end

    def authorize_project_access!(user_id, project_id)
      # Replace with actual data logic to check membership
      allowed_project_ids = Project.where(user_id: user_id).pluck(:id)
      error!('Forbidden', 403) unless allowed_project_ids.include?(project_id.to_i)
    end
  end

  before { authenticate_bearer! }

  desc 'Get project for user'
  params do
    requires :user_id, type: Integer
    requires :project_id, type: Integer
  end
  get 'users/:user_id/projects/:project_id' do
    authorize_project_access!(params[:user_id], params[:project_id])
    { project_id: params[:project_id], name: 'Internal Docs' }
  end
end

General remediation guidance

  • Always compare the authenticated subject (from the Bearer Token) with the object ID in the route, not just presence of a token.
  • For indirect references or UUIDs, map the token’s subject to a database record and verify ownership before loading data.
  • Use shared authorization helpers to keep checks consistent across endpoints.
  • Avoid trusting client-supplied IDs without confirming alignment with the token’s subject or with server-side relationships.

These patterns reduce the risk of BOLA by ensuring that Bearer Token authentication is coupled with object-level authorization, rather than relying on the token alone to gate access.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Why does using a Bearer Token alone not prevent BOLA in Grape APIs?
A Bearer Token proves identity but does not enforce object-level permissions. If an endpoint uses a separate resource ID (e.g., :id) and does not verify that the token’s subject is allowed to access that specific ID, an attacker can change the ID to access other resources, resulting in BOLA.
Can middleBrick detect BOLA in Grape APIs that use Bearer Tokens?
Yes. middleBrick tests unauthenticated attack surfaces and, when provided with a Bearer Token, runs authenticated probes to check whether object-level authorization correctly scopes resources to the token’s subject. Findings highlight missing ownership checks and provide remediation guidance.