HIGH broken authenticationhanami

Broken Authentication in Hanami

How Broken Authentication Manifests in Hanami

Broken Authentication in Hanami applications often stems from misconfigured session handling, weak password policies, and improper authorization checks. Hanami's modular architecture means authentication vulnerabilities can appear in specific contexts that developers might overlook.

One common manifestation occurs in Hanami's controller actions where developers forget to check authentication before sensitive operations. Consider this vulnerable pattern:

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

    def call(params)
      user = UserRepository.find(params[:id])
      user.update(params[:user])
      UserRepository.update(user)
    end
  end
end

This code allows any authenticated user to update any other user's profile by simply changing the ID parameter—a classic Broken Object Level Authorization (BOLA) issue. Hanami doesn't enforce authentication by default, so developers must explicitly add guards.

Session fixation attacks are particularly relevant in Hanami applications using cookie-based sessions. If your application doesn't regenerate session IDs after login, attackers can fixate a session ID before authentication and hijack the session afterward:

module Web::Controllers::Sessions
  class Create
    include Web::Action

    def call(params)
      user = UserRepository.find_by_email(params[:email])
      if user && user.authenticate(params[:password])
        session[:user_id] = user.id
        redirect_to routes.root_path
      else
        self.status = 401
      end
    end
  end
end

The missing session.regenerate! call makes this vulnerable to session fixation. Hanami's session middleware provides this method, but developers must remember to use it.

Password storage is another critical area. Hanami doesn't enforce any specific password hashing strategy, and developers might accidentally store passwords in plaintext or use weak hashing:

# VULNERABLE: Plaintext password storage
class UserRepository
  def create(data)
    data[:password] = data[:password] # Storing plaintext!
    entities::User.new(data)
  end
end

Even when using Hanami's built-in Hanami::Model::Password module, improper configuration can lead to vulnerabilities:

class User < Hanami::Entity
  include Hanami::Model::Password

  attribute :password, Hanami::Model::Password::Digest
end

This is secure, but developers might override it with custom, insecure implementations.

Timing attacks on authentication are subtle but dangerous in Hanami applications. When authentication checks return different responses based on whether an email exists or not, attackers can enumerate valid accounts:

# VULNERABLE: Account enumeration possible
module Web::Controllers::Sessions
  class Create
    def call(params)
      user = UserRepository.find_by_email(params[:email])
      if user.nil?
        # Response time differs
        sleep(0.1)
        self.status = 401
      elsif user.authenticate(params[:password])
        session[:user_id] = user.id
        redirect_to routes.root_path
      else
        self.status = 401
      end
    end
  end
end

Rate limiting is often overlooked in Hanami applications. Without proper rate limiting on authentication endpoints, applications become vulnerable to brute-force attacks:

# VULNERABLE: No rate limiting
module Web::Controllers::Sessions
  class Create
    include Web::Action

    def call(params)
      # Unlimited login attempts
      # Attacker can brute-force indefinitely
    end
  end
end

Hanami-Specific Detection

Detecting Broken Authentication in Hanami applications requires examining both the code structure and runtime behavior. Hanami's explicit controller pattern makes authentication vulnerabilities relatively easy to spot once you know what to look for.

Code analysis should focus on controller actions that handle authentication, session management, and user data modification. Look for these patterns:

# Check for missing authentication guards
module Web::Controllers::Accounts
  class Show
    include Web::Action

    def call(params)
      # MISSING: authentication check before accessing user data
      account = AccountRepository.find(params[:id])
      self.body = account.to_h.to_json
    end
  end
end

middleBrick's black-box scanning approach is particularly effective for Hanami applications because it tests the actual API endpoints without requiring source code access. The scanner examines authentication flows by attempting to access protected resources without credentials, testing session fixation vulnerabilities, and checking for broken authorization controls.

For Hanami applications, middleBrick specifically tests:

  • Authentication bypass attempts on protected endpoints
  • Session fixation by reusing session IDs
  • BOLA vulnerabilities by modifying resource IDs in requests
  • Password policy weaknesses by testing common password patterns
  • Rate limiting effectiveness on authentication endpoints
  • Information disclosure in authentication error messages

The CLI tool makes it easy to scan your Hanami API endpoints:

npx middlebrick scan https://your-hanami-app.com/api/v1/users

# Or integrate into your CI/CD pipeline
npx middlebrick scan --threshold B --fail-on-worse https://staging.your-app.com

middleBrick's OpenAPI analysis is particularly valuable for Hanami applications since Hanami supports OpenAPI spec generation. The scanner cross-references your API documentation with actual runtime behavior, identifying discrepancies between documented authentication requirements and implemented security.

Runtime detection should include monitoring for suspicious authentication patterns:

# Add logging to detect authentication anomalies
module Web::Controllers::Sessions
  class Create
    def call(params)
      # Log authentication attempts for monitoring
      logger.info "Authentication attempt from #{request.ip} for #{params[:email]}"

Hanami's middleware stack allows you to add authentication monitoring middleware that tracks authentication failures and suspicious patterns across your application.

Hanami-Specific Remediation

Hanami provides several native features and patterns for fixing Broken Authentication vulnerabilities. The key is leveraging Hanami's modular architecture and built-in security features rather than implementing custom authentication logic.

First, implement proper authentication guards using Hanami's before filters:

module Web::Controllers::Users
  class Update
    include Web::Action
    before :authenticate!

    def call(params)
      user = UserRepository.find(params[:id])
      halt 403 unless user.id == current_user.id || current_user.admin?
      user.update(params[:user])
      UserRepository.update(user)
    end

    private

    def authenticate!
      halt 401 unless session[:user_id]
    end
  end
end

For session fixation protection, always regenerate session IDs after successful authentication:

module Web::Controllers::Sessions
  class Create
    include Web::Action

    def call(params)
      user = UserRepository.find_by_email(params[:email])
      if user && user.authenticate(params[:password])
        session.regenerate! # CRITICAL: prevents session fixation
        session[:user_id] = user.id
        redirect_to routes.root_path
      else
        self.status = 401
      end
    end
  end
end

Hanami's password handling should use the built-in Hanami::Model::Password module with proper configuration:

class UserRepository
  include Hanami::Repository

  # Use strong hashing with proper cost factors
  class << self
    def create(data)
      data[:password_digest] = Hanami::Model::Password.create(data[:password])
      entity = entities::User.new(data)
      persist(entity)
    end
  end
end

Implement constant-time authentication checks to prevent timing attacks:

module Web::Controllers::Sessions
  class Create
    include Web::Action

    def call(params)
      user = UserRepository.find_by_email(params[:email]) || dummy_user
      authenticated = user.authenticate(params[:password])
      
      if authenticated
        session.regenerate!
        session[:user_id] = user.id
        redirect_to routes.root_path
      else
        # Always take same time regardless of outcome
        sleep(0.1) if user == dummy_user
        self.status = 401
      end
    end

    private

    def dummy_user
      # Dummy user with precomputed hash for timing consistency
      User.new(password_digest: User::DUMMY_HASH)
    end
  end
end

Rate limiting is crucial for authentication endpoints. Hanami doesn't include rate limiting by default, but you can add Rack middleware:

# config.ru
require "rack/attack"

class RateLimit
  def initialize(app)
    @app = app
  end

  def call(env)
    req = Rack::Request.new(env)
    
    if req.path.match(%r{/api/v1/sessions})
      # Limit to 5 attempts per IP per 15 minutes
      if Rack::Attack.throttle("logins/ip", req.ip, 5, 900)
        return [429, { 'Content-Type' => 'application/json' }, [{ error: 'Rate limit exceeded' }.to_json]]
      end
    end
    
    @app.call(env)
  end
end

For comprehensive authentication security, implement multi-factor authentication using Hanami's extensibility:

module Web::Controllers::Sessions
  class Create
    include Web::Action

    def call(params)
      user = UserRepository.find_by_email(params[:email])
      if user && user.authenticate(params[:password])
        if user.mfa_enabled?
          session[:mfa_pending] = user.id
          redirect_to routes.mfa_path
        else
          complete_login(user)
        end
      else
        self.status = 401
      end
    end

    private

    def complete_login(user)
      session.regenerate!
      session[:user_id] = user.id
      redirect_to routes.root_path
    end
  end
end

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

How does Hanami's authentication differ from Rails?
Hanami uses a more explicit, modular approach where authentication must be manually implemented in each controller action. Unlike Rails' Devise gem that provides comprehensive authentication out of the box, Hanami gives developers more control but requires them to implement security patterns themselves. This explicitness actually helps prevent some Broken Authentication issues since you must consciously add authentication checks rather than relying on convention.
Can middleBrick scan my Hanami application without access to the source code?
Yes, middleBrick performs black-box scanning that tests your Hanami API endpoints without requiring source code access. It sends requests to your running application, analyzes responses, and identifies authentication vulnerabilities like missing authorization checks, session fixation opportunities, and broken access controls. This makes it perfect for testing staging environments or third-party Hanami APIs.