HIGH identification failureshanamiapi keys

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

Frequently Asked Questions

What does middleBrick check for when testing API keys in Hanami?
middleBrick checks whether API keys are accepted without strict identification, whether keys are validated consistently across endpoints, and whether key usage is scoped to a specific actor. It also tests for missing key binding to user or tenant context and verifies that keys are not shared in ways that obscure ownership.
Can Hanami API keys be safely passed in query strings?
No. Passing API keys in query strings exposes them in logs, browser history, and referrer headers. Always transmit API keys in request headers over TLS and store only their hashes server-side.