GuardAPI

Fix BOLA (Broken Object Level Authorization) in Hanami

Broken Object Level Authorization (BOLA) remains the most critical vulnerability in the API security landscape. In Hanami, this usually manifests when an Action fetches a resource directly from a Repository using a raw ID from request parameters without verifying ownership. To kill BOLA, you must shift from global lookups to scoped queries that enforce ownership at the database level.

The Vulnerable Pattern

module Web::Actions::Invoices
  class Show < Web::Action
    def handle(req, res)
      # VULNERABLE: Repository finds any record by ID.
      # Attacker can iterate :id to steal other users' data.
      repo = InvoiceRepository.new
      invoice = repo.find(req.params[:id])
  halt 404 unless invoice
  res.body = invoice.to_json
end

end end

The Secure Implementation

The vulnerability stems from trusting the user-provided ID as a global pointer. The fix implements Scoped Repository Lookups. By forcing the database query to include the 'user_id' in the WHERE clause, you ensure that even if an attacker guesses a valid object ID, the database will return null unless that object belongs to the requester. Always use .one or .first on a filtered relation rather than a generic .find(id) to ensure authorization is baked into the data retrieval layer.

module Web::Actions::Invoices
  class Show < Web::Action
    def handle(req, res)
      # SECURE: Lookup is scoped to the authenticated user's ID.
      user_id = req.env['current_user'].id
      repo = InvoiceRepository.new
  # Query includes the owner context
  invoice = repo.find_by_user(id: req.params[:id], user_id: user_id)

  halt 404 unless invoice
  res.body = invoice.to_json
end

end end

In InvoiceRepository

class InvoiceRepository < Hanami::Repository def find_by_user(id:, user_id:) invoices.where(id: id, user_id: user_id).one end end

About this page

Framework notes in /guides are generated sketches kept for URL stability. They are not human pentest reports and they are not GuardAPI scan output. The product is a GET-only BOLA merge gate. Maintained by GuardAPI. Questions: [email protected]