HIGH insecure designhanamibearer tokens

Insecure Design in Hanami with Bearer Tokens

Insecure Design in Hanami with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Insecure design in a Hanami application often arises when authentication decisions are embedded in the application architecture in a way that makes token validation inconsistent or bypassable. When Bearer tokens are used without a robust, centralized authorization strategy, the API surface expands in ways that violate the principle of least privilege.

Hanami, being a web framework that favors explicit routing and modular architecture, can inadvertently expose endpoints if routes are defined without enforcing token validation for sensitive actions. For example, an endpoint like POST /api/v1/admin/users might be implemented in a controller without verifying that the incoming Bearer token has admin-scoped claims. Because Hanami encourages separating concerns, developers might place authentication logic in a separate action or middleware but forget to apply it uniformly across all resource controllers.

Another insecure design pattern involves how token scope is interpreted. If the application trusts the presence of a Bearer token without validating the scope or role encoded within it, an authenticated context is incorrectly treated as an authorized one. This becomes critical when token introspection is not performed against an identity provider or when tokens are issued with broad permissions for convenience during development and never revisited.

Consider an API route defined as:

module Web::Controllers::Admin
  class Users
    include Web::Action

    def call(params)
      # Insecure design: No token validation or scope check
      user = UserRepository.new.create(params[:user])
      Response.new(201, {}, user.to_json)
    end
  end
end

In this scenario, any request reaching this route with a valid Bearer token in the Authorization header is processed, regardless of whether the token represents an admin user. The insecure design lies in assuming that routing to this controller implies proper authorization, rather than explicitly verifying token claims.

Additionally, Hanami’s support for multiple API versions can compound the risk if older versions of endpoints are not retired or properly secured. An API route like /api/v1/resources might be deprecated, but if it remains accessible and lacks Bearer token enforcement, it becomes a pathway for privilege escalation or data exposure. The framework does not enforce security by default, so the burden falls on the developer to ensure every route requiring protection includes appropriate checks.

Insecure design also manifests in how tokens are stored and transmitted. If a Hanami frontend or client-side JavaScript bundle includes hardcoded Bearer tokens or exposes token endpoints without rate limiting or input validation, it increases the attack surface. Tokens might be leaked through logs, error messages, or browser developer tools when debugging endpoints that lack proper access controls.

Finally, the combination of Hanami’s flexible routing and Bearer token usage without mandatory scope validation can lead to horizontal privilege issues. Two users with similar roles might access each other’s data because the API endpoints do not enforce ownership checks at the design level, relying only on the presence of a token rather than a comprehensive policy model.

Bearer Tokens-Specific Remediation in Hanami — concrete code fixes

To remediate insecure design issues with Bearer tokens in Hanami, implement centralized authorization checks and validate token claims before allowing access to sensitive endpoints. Below are concrete code examples demonstrating secure patterns.

1. Centralized Authentication Action

Create a base action that validates the Bearer token and enforces scope requirements. This ensures consistent security across controllers.

module Web::Actions::Authenticated
  def self.included(base)
    base.class_eval do
      before :authorize_bearer_token
    end
  end

  def authorize_bearer_token
    auth_header = request.env['HTTP_AUTHORIZATION']
    unless auth_header&.start_with?('Bearer ')
      halt 401, {}, { error: 'Unauthorized' }.to_json
    end

    token = auth_header.split(' ').last
    # Validate token with identity provider or introspection endpoint
    decoded = validate_token(token)
    if decoded.nil?
      halt 403, {}, { error: 'Forbidden: Invalid token' }.to_json
    end

    # Enforce scope for admin routes
    if route_settings[:required_scope] && !decoded['scope'].to_s.split.include?(route_settings[:required_scope])
      halt 403, {}, { error: 'Insufficient scope' }.to_json
    end

    @current_user = decoded
  end

  def validate_token(token)
    # Replace with actual validation logic, e.g., call to auth server
    # This is a simplified example
    return { 'scope' => 'admin read write', 'sub' => 'user-123' } if token == 'valid_admin_token'
    return { 'scope' => 'read', 'sub' => 'user-456' } if token == 'valid_read_token'
    nil
  end
end

2. Applying Authentication to Specific Controllers

Include the authenticated action in controllers that require Bearer token validation and specify required scopes where necessary.

module Web::Controllers::Admin::Users
  class Create
    include Web::Action
    include Web::Actions::Authenticated

    # Require admin scope for this endpoint
    route_settings required_scope: 'admin'

    def call(params)
      user = UserRepository.new.create(params[:user])
      Response.new(201, {}, user.to_json)
    end
  end

3. Token Validation Middleware Alternative

For broader protection, implement a middleware that checks Bearer tokens before routing reaches Hanami controllers.

module Web::Middleware::BearerAuth
  def initialize(app)
    @app = app
  end

  def call(env)
    request = Rack::Request.new(env)
    if request.path.start_with?('/api/')
      auth_header = request.env['HTTP_AUTHORIZATION']
      if auth_header&.start_with?('Bearer ')
        token = auth_header.split(' ').last
        decoded = validate_token(token)
        if decoded
          env['warden'] = { user: decoded, scope: decoded['scope'] }
        else
          return [401, { 'Content-Type' => 'application/json' }, [{ error: 'Invalid token' }.to_json]]
        end
      else
        return [401, { 'Content-Type' => 'application/json' }, [{ error: 'Missing token' }.to_json]]
      end
    end
    @app.call(env)
  end

  private

  def validate_token(token)
    # Integration with identity provider
    { 'sub' => 'user-id', 'scope' => 'read write' } if token.length > 10
  end
end

# config/initializers/middleware.rb
require_relative 'middleware/bearer_auth'
Rack::Builder.new do
  use Web::Middleware::BearerAuth
  run Hanami.app
end

4. Scope-Based Authorization in Controller Logic

Even after validating the token, enforce scope checks within business logic to prevent horizontal access violations.

module Web::Controllers::Resources
  class Show
    include Web::Action
    include Web::Actions::Authenticated

    def call(params)
      resource = ResourceRepository.find(params[:id])
      # Ensure user scope aligns with resource ownership or permissions
      unless current_user_can_access?(@current_user, resource)
        halt 403, {}, { error: 'Access denied to resource' }.to_json
      end
      Response.new(200, {}, resource.to_json)
    end

    private

    def current_user_can_access?(user, resource)
      # Implement policy: users can only access their own resources unless admin
      user['scope'].to_s.split.include?('admin') || user['sub'] == resource.user_id
    end
  end
end

5. Example of a Valid Bearer Token Request

Ensure clients send tokens correctly and that the server validates format strictly.

curl -X GET "http://localhost:2300/api/v1/admin/users" \
  -H "Authorization: Bearer valid_admin_token" \
  -H "Content-Type: application/json"

6. Rejecting Malformed or Missing Tokens

Return clear error messages without exposing internal details.

def authorize_bearer_token
  auth_header = request.env['HTTP_AUTHORIZATION']
  unless auth_header&.match?(/^Bearer [A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+/?[A-Za-z0-9\-_.+/=]*$/)
    halt 400, {}, { error: 'Malformed authorization header' }.to_json
  end
  # ... continue validation
end

Frequently Asked Questions

How does Hanami's modular design affect Bearer token security?
Hanami's modular design requires developers to explicitly add authentication to each controller or action. Without a centralized pattern, it is easy to forget to enforce Bearer token validation on sensitive endpoints, leading to insecure design where routes are accessible despite authentication being available elsewhere.
What is the risk of relying solely on Bearer token presence without scope validation in Hanami?
Relying only on the presence of a Bearer token without validating its scope can cause privilege escalation and horizontal access violations. An attacker with a low-privilege token might access admin endpoints if the application does not check the token's scope against the required permissions for each route.