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 ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |