Api Rate Abuse in Hanami (Ruby)
Api Rate Abuse in Hanami with Ruby — how this specific combination creates or exposes the vulnerability
Hanami is a Ruby web framework that emphasizes explicit architecture and minimal runtime magic. When building APIs with Hanami, developers often rely on the framework’s composable objects—such as Action, Router, and View—to structure endpoints. While this design promotes clarity, it does not inherently enforce request-rate controls at the application layer. Without explicit limits, an API endpoint written in Hanami with Ruby can be invoked repeatedly in a short time window, enabling rate abuse such as credential stuffing, scraping, or denial-of-service via resource exhaustion.
The risk is amplified because Hanami’s default setup does not include middleware for throttling. In contrast to some full-stack frameworks that ship with built-in Rack-based rate limiting, Hanami leaves this responsibility to the developer. An attacker can send many unauthenticated or authenticated requests to endpoints such as /api/v1/users or /api/v1/auth/login, targeting either the application server (e.g., Puma) or downstream dependencies like databases or external services. This maps to the BFLA/Privilege Escalation and Rate Limiting checks in middleBrick’s 12 security checks, which detect missing or insufficient rate controls during unauthenticated scanning.
Furthermore, Hanami applications that expose GraphQL or custom JSON APIs may compound the issue if query complexity or depth is not bounded. An attacker can craft deeply nested queries or batch large payloads, consuming CPU and memory without triggering errors. Because middleBrick tests unauthenticated attack surfaces and runs checks in parallel—including Rate Limiting and Input Validation—it can identify whether an endpoint responds with identical or similar data regardless of request frequency, and whether inputs are accepted without constraint. The scanner also cross-references OpenAPI/Swagger specs (2.0, 3.0, 3.1) with runtime behavior, so discrepancies such as undocumented endpoints or missing rate-limit headers are surfaced as actionable findings.
Real-world attack patterns mirror these risks. For example, an endpoint lacking per-client throttling could be exploited to perform automated enumeration of user identifiers, contributing to account takeover workflows aligned with the OWASP API Top 10 category ‘2023-A5: Security Misconfiguration.’ If authentication tokens are issued after login, weak or absent rate limits on the authentication route can enable token brute-forcing, a scenario reflected in findings tied to Authentication and BOLA/IDOR. middleBrick’s detection approach does not rely on internal architecture details; instead, it observes behavior—status codes, response times, and data returned—to infer whether rate abuse is feasible.
Because Hanami encourages developers to keep components small and testable, adding instrumentation for rate control is straightforward when done deliberately. However, without conscious design, a Hanami + Ruby API can drift into vulnerable states. middleBrick’s scanning approach—using 12 security checks across authentication, input validation, and rate limiting—helps surface these gaps early. Developers can then implement targeted remediation, such as introducing throttling at the routing or service layer, ensuring that protections are aligned with actual usage patterns and compliance expectations like PCI-DSS and SOC2.
Ruby-Specific Remediation in Hanami — concrete code fixes
To remediate rate abuse in Hanami applications built with Ruby, implement explicit rate-limiting logic at the appropriate layer, typically within the action or via Rack middleware. Avoid relying on framework defaults. Below are concrete, syntactically correct examples tailored to Hanami’s architecture.
1. Rack-based throttling with Rack::Attack
Use Rack::Attack to enforce global limits before requests reach Hanami’s router. This approach works across all endpoints and is transparent to Hanami’s internal objects.
# config/initializers/rack_attack.rb
if defined?(Rack::Attack)
Rack::Attack.throttle('req/ip', limit: 30, period: 60) do |req|
req.ip
end
Rack::Attack.throttle('login/ip', limit: 5, period: 60) do |req|
if req.path == '/api/v1/auth/login' && req.post?
req.ip
end
end
Rack::Attack.throttle('user/identifier', limit: 10, period: 60) do |req|
if req.path =~ %r{^/api/v1/users/} && req.get?
req.params['id'] || req.path
end
end
Rack::Attack.throttle('graphql/complexity', limit: 100, period: 60) do |req|
if req.path == '/api/v1/graphql' && req.post?
# Simplified: use query AST analysis in production
req.ip
end
end
Rack::Attack.throttled_response = lambda do |env|
match = Rack::Attack.cache.read(env['rack.attack.match_data'].cache_key])
[429, { 'Content-Type' => 'application/json' }, [{ error: 'Too many requests', retry_after: env['rack.attack.match_data'].period }.to_json]]
end
end
2. Service object encapsulation in Hanami
Encapsulate rate logic inside a Hanami service object to keep actions lean and testable.
# lib/api/rate_limiter.rb
class Api::RateLimiter
def initialize(identity) @identity = identity end
def allowed?
key = "rate_limit:#{@identity}:#{Time.now.to_i / 60}"
count = Hanami.redis { |it| it.incr(key) }
Hanami.redis { |it| it.expire(key, 60) } if count == 1
count <= 10
end
end
# app/actions/api/users/show.rb
class Api::Users::Show
include Hanami::Action
def call(params)
limiter = Api::RateLimiter.new(params[:id])
unless limiter.allowed?
self.status = 429
self.body = { error: 'Rate limit exceeded' }.to_json
return
end
# proceed with business logic
user = UserRepository.new.find(params[:id])
self.body = UserPresenter.new(user).serializable_hash.to_json
end
end
3. Middleware or plugin integration
If you prefer a reusable component, create a lightweight Hanami plugin that injects a before action hook.
# lib/middlebrick/rate_guard.rb
module Middlebrick
module RateGuard
def self.included(base)
base.class_eval do
before :validate_rate_limit, if: :api_request?
end
end
def validate_rate_limit
return unless current_user || request.ip
key = "hb_rate:#{request.ip}:#{params.fetch('controller')}:#{params.fetch('action')}"
current = Hanami.redis { |it| it.get(key) }&.to_i || 0
if current >= 5
self.status = 429
self.body = { error: 'Rate limit exceeded' }.to_json
halt
else
Hanami.redis { |it| it.incr(key) }
Hanami.redis { |it| it.expire(key, 60) }
end
end
def api_request?
request.path.start_with?('/api/')
end
end
end
# app/actions/application_action.rb
class ApplicationAction < Hanami::Action
include Middlebrick::RateGuard
end
These examples demonstrate how to integrate rate limiting within Hanami’s object-oriented design using Ruby. They map to the remediation guidance in middleBrick findings, helping you address Rate Limiting and BFLA/IDOR concerns. After applying controls, re-run middleBrick scans to confirm that endpoints now exhibit appropriate throttling behavior and that findings are resolved.