Brute Force Attack in Hanami
How Brute Force Attack Manifests in Hanami
In a Hanami application, brute force attacks typically target authentication endpoints, such as login or password reset routes, where an attacker iterates through credential combinations to gain unauthorized access. Hanami's modular architecture, with separate actions and entities, can inadvertently expose these endpoints if rate limiting is not explicitly enforced at the action level.
A common vulnerable pattern is a SessionsController with a create action that processes POST requests to /sessions without throttling. For example, a naive implementation might directly authenticate via a Hanami model (using ROM or Sequel) and establish a session on success:
# apps/web/controllers/sessions_controller.rb
module Web
module Controllers
module Sessions
class Create
include Web::Action
def call(params)
user = UserRepository.new.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect to: '/dashboard'
else
flash[:error] = 'Invalid credentials'
redirect to: '/login'
end
end
end
end
end
endThis action lacks any mechanism to limit request frequency. An attacker can script thousands of attempts per minute against /sessions using tools like hydra or burp suite. Because Hanami does not impose global rate limits by default, each request is processed independently, allowing unlimited guesses. The vulnerability is exacerbated if the application reveals whether an email exists (via distinct error messages), enabling user enumeration before the brute force phase.
Additionally, Hanami's use of hanami-action means that without middleware or action-specific throttles, the Rack stack receives unfiltered traffic. Attackers may also target API endpoints that use token-based authentication (e.g., JWT) if no rate limiting exists on the token issuance endpoint (/auth/token), leading to token spraying attacks.
Hanami-Specific Detection
Detecting brute force vulnerabilities in Hanami requires examining both code and runtime behavior. Manually, review all authentication-related actions (e.g., SessionsController#create, PasswordResetsController#update) for the absence of throttling mechanisms. Look for missing throttle declarations or custom Rack middleware that limits requests per IP or per account.
Automated scanning with middleBrick identifies this issue through its Rate Limiting check (one of 12 parallel tests). When you submit a Hanami app's login endpoint URL (e.g., https://your-app.com/sessions), middleBrick sends a rapid series of POST requests with invalid credentials and analyzes the HTTP responses. A lack of 429 Too Many Requests status codes or inconsistent throttling headers (Retry-After) indicates a vulnerability. The scan takes 5–15 seconds and produces a per-category breakdown, highlighting the Rate Limiting risk with severity and remediation guidance specific to Hanami.
You can also integrate this detection into your CI/CD pipeline using middleBrick's GitHub Action. By adding the action to your workflow, you can automatically scan staging Hanami APIs before deployment and fail the build if the rate limiting score drops below your threshold. For terminal-based checks, the middleBrick CLI (npm install -g middlebrick) allows you to run middlebrick scan https://your-hanami-app.com/sessions and get instant JSON output.
Hanami-Specific Remediation
Remediate brute force vulnerabilities in Hanami by implementing request throttling directly in your actions using the hanami-action gem's built-in throttle method. This method limits requests based on IP, session, or custom keys. Apply it to all authentication endpoints.
Here is a corrected SessionsController#create with IP-based throttling (5 attempts per 60 seconds):
# apps/web/controllers/sessions_controller.rb
module Web
module Controllers
module Sessions
class Create
include Web::Action
throttle limit: 5, period: 60, by: :ip
def call(params)
user = UserRepository.new.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect to: '/dashboard'
else
flash[:error] = 'Invalid credentials'
redirect to: '/login'
end
end
end
end
end
endFor account-specific locking (preventing attacks on a single user account), combine throttling with a failure counter in your user entity/repository. After n failed attempts, disable the account temporarily:
# apps/web/entities/user.rb
module Web
module Entities
class User < Hanami::Entity
attributes :email, :password_digest, :locked_at, :failed_attempts
def lock!
update(locked_at: Time.now, failed_attempts: 0)
end
def increment_failed_attempts!
update(failed_attempts: (failed_attempts || 0) + 1)
lock! if failed_attempts >= 5
end
end
end
endThen, in your controller, call user.increment_failed_attempts! on authentication failure. Also, ensure your login form does not reveal whether an email exists—use a generic error message like "Invalid credentials" for both missing users and wrong passwords.
After implementing these fixes, rescan your endpoints with middleBrick to verify the Rate Limiting score improves. For ongoing enforcement, use middleBrick's Pro plan to enable continuous monitoring, which periodically rescans your Hanami APIs and alerts your team via Slack or email if throttling regresses.