HIGH credential stuffinghanami

Credential Stuffing in Hanami

How Credential Stuffing Manifests in Hanami

Credential stuffing attacks against Hanami applications typically target the framework's default session-based authentication endpoints, such as /sessions (created by hanami new). Attackers use automated tools to submit large volumes of previously breached username/password pairs, exploiting the fact that many users reuse credentials across sites. In a Hanami context, the vulnerability often stems from three specific code patterns:

  • Missing Rate Limiting on Login Actions: Hanami actions (classes inheriting from Hanami::Action) have no built-in throttling. A standard login action like Actions::Sessions#call will process unlimited requests unless explicitly protected, allowing brute-force attempts.
  • Insecure Session Configuration: Hanami's default session middleware (cookie-based) may lack security flags if not configured. For example, sessions without the secure flag are transmitted over HTTP, enabling session hijacking after a successful stuffing attempt.
  • Verbose Error Messages: Hanami's default authentication code often returns distinct messages for "user not found" vs. "invalid password" (e.g., halt 401, 'Invalid email or password' vs. halt 401, 'User not found'). This allows attackers to enumerate valid accounts by observing response differences, increasing stuffing efficiency.

Consider this vulnerable Hanami login action from a typical app generated by the Hanami CLI:

module MyApp
  module Actions
    class Sessions
      include MyApp::Dependencies[repo: "repositories.users"]

      def call(params)
        user = repo.find_by_email(params[:email])
        if user && user.password == params[:password]
          session[:user_id] = user.id
          redirect to: "/dashboard"
        else
          halt 401, "Invalid credentials"
        end
      end
    end
  end
end

This implementation lacks: (1) any request throttling, (2) uniform error messaging, and (3) session security guarantees. An attacker can script thousands of credential pairs against /sessions with no deterrent.

Hanami-Specific Detection

Detecting credential stuffing vulnerabilities in a Hanami API requires testing the authentication endpoint's behavior under repeated requests. middleBrick's black-box scanner automates this by:

  1. Targeting Hanami's Conventional Routes: It probes common Hanami authentication paths like /sessions, /login, or custom routes defined in config/routes.rb (e.g., post "/api/v1/auth", to: "actions.sessions#call").
  2. Testing Rate Limiting Responses: The scanner sends sequential login attempts (using a test credential set) and monitors HTTP status codes. A properly rate-limited endpoint should return 429 Too Many Requests after a threshold (e.g., 5 attempts per IP per minute). Absence of 429 responses indicates a missing throttle.
  3. Analyzing Session Cookies: middleBrick inspects the Set-Cookie header from successful authentication responses. It checks for Secure, HttpOnly, and SameSite attributes. A Hanami app configured without secure: true in config/sessions.rb will have cookies flagged as insecure.
  4. Probing Error Message Consistency: The scanner submits both valid and invalid credentials, comparing response bodies and timing. Identical status codes and response lengths for "user not found" and "wrong password" indicate proper mitigation against account enumeration.

In middleBrick's report, these findings appear under the Authentication and Rate Limiting categories. For a Hanami app, a typical finding might read: "Login endpoint lacks rate limiting. Attackers can attempt unlimited credentials. Implement Rack::Attack throttling on the session creation route." The scanner correlates this with OWASP API Top 10:2023 A2: Broken Authentication and maps it to compliance frameworks like PCI-DSS 8.2.4.

Hanami-Specific Remediation

Remediating credential stuffing in Hanami involves three native framework adjustments. All changes are made in your application code—middleBrick only detects and reports the issue.

1. Add Rate Limiting with Rack::Attack

Hanami, being Rack-based, integrates seamlessly with the rack-attack gem. Add it to your Gemfile:

gem "rack-attack"

Then create an initializer (config/initializers/rack_attack.rb) to throttle the login action:

class Rack::Attack
  # Throttle login attempts by IP (5 requests per 20 seconds)
  throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
    if req.path == '/sessions' && req.post?
      req.ip
    end
  end

  # Throttle by email to prevent account-specific brute force
  throttle('logins/email', limit: 5, period: 20.seconds) do |req|
    if req.path == '/sessions' && req.post? && req.params['email']
      # Normalize email case
      req.params['email'].to_s.downcase
    end
  end
end

Finally, insert the middleware in config.ru:

require_relative './config/initializers/rack_attack'
use Rack::Attack
run MyApp

2. Enforce Secure Session Cookies

Configure Hanami's session middleware in config/sessions.rb to use secure, HTTP-only cookies with a strict SameSite policy:

MyApp.configure do
  # ...

  sessions :cookie,
    key: 'myapp.session',
    secure: production?,   # Only send over HTTPS in production
    httponly: true,        # Prevent JavaScript access
    same_site: :lax,      # Mitigate CSRF; use :strict for higher security
    secret: ENV['SESSION_SECRET']
end

Ensure production? returns true in your deployment environment (Hanami sets this automatically via HANAMI_ENV).

3. Uniform Authentication Errors

Modify the login action to return identical responses for all authentication failures. This prevents user enumeration:

def call(params)
  user = repo.find_by_email(params[:email])
  # Always perform password check if user exists to ensure equal timing
  authenticated = user && user.password == params[:password]

  if authenticated
    session[:user_id] = user.id
    redirect to: "/dashboard"
  else
    # Use a generic message and identical response structure
    halt 401, "Invalid email or password"
  end
end

For enhanced timing attack resistance, introduce a constant-time delay (e.g., sleep(0.1)) on failure paths, though Hanami's ROM queries may already introduce variability.

After applying these fixes, rescan with middleBrick. The Authentication and Rate Limiting category scores should improve, and the overall risk grade will rise accordingly.

FAQ

Q: How does middleBrick specifically detect credential stuffing vulnerabilities in a Hanami API?
A: middleBrick's scanner targets your Hanami authentication endpoints (e.g., /sessions) and sends multiple sequential login attempts using test credentials. It checks for HTTP 429 responses (indicating rate limiting), inspects session cookie security flags (Secure, HttpOnly), and analyzes error message consistency. Missing rate limits, insecure cookies, or enumeration-prone errors are reported as findings under the Authentication and Rate Limiting categories.

Q: What Hanami-specific code changes prevent credential stuffing attacks?
A: Implement three fixes: (1) Add rack-attack throttling to your config/initializers/rack_attack.rb to limit login attempts by IP and email; (2) Configure secure sessions in config/sessions.rb with secure: true, httponly: true, and a strong same_site policy; (3) Modify your login action (e.g., Actions::Sessions) to return generic "Invalid email or password" messages for all failure cases. These are native Hanami/Rack configurations—no external agents required.