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
endThis 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
endThe 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
endEven 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
endThis 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
endRate 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
endHanami-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
endmiddleBrick'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
endFor 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
endHanami'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
endImplement 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
endRate 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
endFor 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
endRelated CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |