Broken Access Control in Hanami with Api Keys
Broken Access Control in Hanami with Api Keys — how this specific combination creates or exposes the vulnerability
Broken Access Control occurs when authorization checks are missing or incorrectly enforced, allowing attackers to access or modify resources they should not. In Hanami, this risk is elevated when relying solely on API keys without tying them to a robust authorization model that maps keys to scopes, roles, or tenant boundaries. Hanami encourages explicit, role-based authorization, but if routes or service objects are invoked without validating that the key’s associated principal has permission for the specific resource instance, attackers can manipulate IDs or exploit missing checks to perform BOLA/IDOR.
API keys are often treated as secrets equivalent to authentication, yet they are commonly used only for identification and rate limiting. In Hanami, if a key is validated (e.g., via a repository lookup) but the subsequent authorization step is skipped, an authenticated context is established without proper permissions. For example, an endpoint like /api/v1/accounts/:id might load an account by ID and return it if the API key is valid, without confirming the key’s scope or relationship to that account. This creates a BOLA flaw: changing the numeric ID in the URL can reveal or modify other accounts. Because API keys are often stored and transmitted with lower protection than session cookies, leakage further increases exposure. Attackers can also probe for unauthenticated LLM endpoints if key validation is inconsistent across services, increasing lateral risk.
Another vector arises when Hanami services inadvertently expose internal identifiers or sensitive data in error messages or responses. Without input validation and strict property authorization, an attacker who knows or guesses a valid ID can iterate through values and harvest data. This aligns with the OWASP API Top 10 category for Broken Object Level Authorization and can map to compliance frameworks such as PCI-DSS and SOC2, where access to cardholder or personal data must be tightly controlled. The absence of per-request authorization, even after successful key validation, means the attack surface remains large and scans by tools like middleBrick can detect these gaps through its 12 parallel checks, including BOLA/IDOR and Property Authorization.
Moreover, if rate limiting is applied at the key level but not at the user or role level, a single compromised key can exhaust quotas or enable denial-of-service. Data exposure can occur when keys are logged or echoed in debug output, especially in development environments. Encryption in transit protects the key during transmission, but if the key is accepted via query parameters instead of headers, it may be stored in logs or browser history. SSRF risks increase if the key is used to construct backend requests without validating target hosts. Together, these factors illustrate how Broken Access Control in Hanami with API keys is not just an authentication oversight but a multi-dimensional authorization failure that requires deliberate design and verification.
Api Keys-Specific Remediation in Hanami — concrete code fixes
Remediation centers on ensuring that API key validation is followed by explicit authorization checks scoped to the resource and action. Keys should map to principals with defined roles or scopes, and every data access must verify ownership or permissions. Below are concrete patterns you can adopt in Hanami.
1. Key-to-scope mapping with explicit authorization
Define a service object that resolves the key to an authorized context and enforces policy before data access.
# app/services/authorize_api_key.rb
class AuthorizeApiKey
def initialize(api_key)
@api_key = api_key
end
def call
key_record = Repository::ApiKeys.find_by(token: @api_key)
raise Errors::Unauthorized unless key_record
# Enforce scope-based access
Context.new(
account: key_record.account,
role: key_record.role,
scopes: key_record.scopes
)
end
end
# app/actions/api/accounts/show.rb
class Api::Accounts::Show
include Action
include Import['authorize_api_key']
def call(params)
context = authorize_api_key.call(params[:api_key])
account = Repository::Accounts.find(params[:id])
# BOLA/IDOR check: ensure the account belongs to the key's scope
if context.account.id == account.account_id || context.scopes.include?('admin')
Response::Ok.new(account)
else
Response::Forbidden.new(error: 'access_denied')
end
end
end
2. Policy-based authorization with Hanami's dry-struct
Use policy objects to encapsulate per-instance rules. This keeps authorization logic testable and explicit.
# app/policies/account_policy.rb
class AccountPolicy
attr_reader :context, :account
def initialize(context, account)
@context = context
@account = account
end
def show?
context.role == 'admin' || account.user_id == context.user_id
end
end
# app/actions/api/accounts/show.rb (updated)
class Api::Accounts::Show
include Action
include Import['authorize_api_key']
def call(params)
context = authorize_api_key.call(params[:api_key])
account = Repository::Accounts.find(params[:id])
policy = AccountPolicy.new(context, account)
if policy.show?
Response::Ok.new(account)
else
Response::Forbidden.new(error: 'access_denied')
end
end
end
3. Secure key transmission and storage practices
- Accept API keys only via the
Authorization: ApiKey <token>header to avoid logging in URLs. - Hash keys at rest using a strong one-way function before storing them in the repository.
- Implement rate limiting per key and per user to mitigate abuse.
# config/initializers/rate_limit.rb
Rack::Attack.throttle('api_key/ip', limit: 100, period: 60) do |req|
req.headers['HTTP_AUTHORIZATION']&.split(' ')&.last
end
4. Validation and error handling
Ensure validation rejects malformed keys early and that errors do not leak internal details.
# app/errors.rb
module Errors
class Unauthorized < StandardError; end
class NotFound < StandardError; end
end
# app/actions/base.rb
class BaseAction < Hanami::Action
rescue_from Errors::Unauthorized do |_exc|
self.status = 401
render json: { error: 'invalid_token' }
end
rescue_from Errors::NotFound do |_exc|
self.status = 404
render json: { error: 'not_found' }
end
end
By combining key resolution with per-request authorization checks, Hanami applications can mitigate Broken Access Control while maintaining clarity and auditability. These patterns also align with the findings and remediation guidance that tools like middleBrick provide through its checks for BOLA/IDOR and Property Authorization.