Identification Failures in Hanami with Api Keys
Identification Failures in Hanami with Api Keys — how this specific combination creates or exposes the vulnerability
Identification failures occur when an API cannot reliably determine who is making a request. In Hanami, using API keys as the sole authentication mechanism can create or expose identification failures when keys are not scoped per consumer, are transmitted insecurely, or are validated inconsistently across endpoints.
Hanami is a Ruby web framework that encourages explicit, modular design. When API keys are implemented as request headers without additional context, the system may fail to associate a key with a specific actor, role, or tenant. This becomes an identification failure when multiple services or users share the same key, or when keys are accepted from any header name without strict validation. Because middleBrick tests unauthenticated attack surfaces, it can detect endpoints that accept API keys but do not enforce strict identification, such as missing key binding to a user record or missing checks for key revocation.
Another scenario is that an endpoint validates the presence of an API key but does not verify that the key belongs to the requesting entity. For example, if a Hanami app uses a before action that loads a key but does not ensure the resource being accessed matches the key’s associated scope, an attacker can reference or manipulate other resources by altering identifiers. middleBrick’s BOLA/IDOR and Property Authorization checks are designed to surface these gaps by probing whether a valid key for one user allows access to another user’s data.
Additionally, inconsistent validation across microservices or routes can lead to identification failures. If one Hanami service validates API keys against a central directory and another service only checks for a key’s format, an attacker might bypass identification by routing requests through the less strict endpoint. middleBrick’s authentication checks examine whether the same identification logic is applied uniformly and whether unauthenticated access is possible, even when an API key is present.
In secure designs, API keys should be treated as credentials that bind requests to identities. Hanami applications should pair keys with metadata such as owner ID, allowed scopes, and expiration. Without this linkage, identification becomes ambiguous, and the attack surface grows. middleBrick’s inventory management and data exposure checks help highlight endpoints where keys are accepted without sufficient identification context, and findings include remediation guidance to tighten validation and scoping.
Api Keys-Specific Remediation in Hanami — concrete code fixes
To remediate identification failures when using API keys in Hanami, enforce strict key-to-identity binding and validate keys on every request using a centralized policy. Below are concrete code examples that demonstrate secure handling.
1. Centralized key validation with a dedicated entity and repository
Define an ApiKey entity and a repository that loads and verifies keys, ensuring each key maps to a specific actor and scope.
# entities/api_key.rb
class Entity::ApiKey
include Hanami::Entity
attributes :id, :key_hash, :owner_id, :owner_type, :scopes, :active, :expires_at
alias_method :id, :key_hash
end
# repositories/api_key_repository.rb
class Repository::ApiKeyRepository
def initialize
# In practice, use your ORM (e.g., ROM::Repository)
@db = Hanami::Repository.new
end
def find_by_key(key)
# Use a secure comparison; store key_hash as BCrypt or similar
@db[:api_keys].where(Sequel.expr(key_hash: ApiKeyHasher.digest(key)), active: true).first
end
end
# services/authorize_api_key.rb
class Services::AuthorizeApiKey
def self.call(key, required_scope: nil)
repo = Repository::ApiKeyRepository.new
record = repo.find_by_key(key)
return :invalid_key unless record
return :expired if record[:expires_at] && record[:expires_at] < Time.now
return :insufficient_scope unless has_scope?(record[:scopes], required_scope)
{ actor_id: record[:owner_id], actor_type: record[:owner_type], scopes: record[:scopes] }
end
def self.has_scope?(scopes, required)
return true if required.nil?
scopes.split(',').map(&:strip).include?(required)
end
end
2. Secure header extraction and constant-time comparison
Ensure the key is extracted from a strict header name and compared in a way that avoids timing attacks.
# lib/api_key_auth.rb
module ApiKeyAuth
API_KEY_HEADER = 'HTTP_X_API_KEY'
def self.extract_key(env)
key = env[API_KEY_HEADER]
return nil unless key
# Avoid sending hints via timing; always process a dummy key if missing
key.strip.presence || 'dummy_do_not_use_in_production'
end
end
# In your endpoint or action
key = ApiKeyAuth.extract_key(env)
authorization = Services::AuthorizeApiKey.call(key, required_scope: 'read:data')
if authorization == :invalid_key || authorization == :expired || authorization == :insufficient_scope
halt 401, { error: 'unauthorized' }.to_json
end
@current_actor = authorization
3. Enforce scope and ownership in routes
Use before actions to validate scoping and prevent BOLA by ensuring the requested resource matches the actor derived from the key.
# app/actions/base.rb
class ApplicationAction < Hanami::Action
before :authenticate_api_key
before :authorize_resource, if: -> { params['id'] }
private
def authenticate_api_key
key = ApiKeyAuth.extract_key(env)
result = Services::AuthorizeApiKey.call(key, required_scope: current_scope)
halt 401, { error: 'unauthorized' }.to_json if result.is_a?(Symbol)
@current_actor = result
end
def authorize_resource
# Example: ensure the data_id belongs to the actor's organization
data = DataRepository.new.find(params['id'])
unless data && data.organization_id == @current_actor[:actor_id]
halt 403, { error: 'forbidden' }.to_json
end
end
def current_scope
# Derive required scope from route or endpoint metadata
'read:data'
end
end
4. Rotate keys and avoid shared keys
Design your system so each consumer has a unique key. Implement key rotation and revocation endpoints, and store only key hashes. middleBrick’s authentication and BFLA checks can help verify that keys are not shared across roles or services.
5. Secure transport and storage
Ensure keys are transmitted over TLS and stored hashed in the database. Never log keys or expose them in URLs or query parameters.
# config/initializers/hanami.rb
Hanami.configure do
# Enforce HTTPS in production-related settings
if ENV['RACK_ENV'] == 'production'
# Middleware or reverse proxy should enforce TLS
end
end