Brute Force Attack in Hanami with Cockroachdb
Brute Force Attack in Hanami with Cockroachdb — how this combination creates or exposes the vulnerability
A brute force attack against a Hanami application backed by Cockroachdb typically targets authentication endpoints where account enumeration or weak rate limiting allows an attacker to make repeated guesses against user credentials. Hanami’s routing and controller design can unintentionally expose timing differences between valid and invalid users, especially when database queries for user existence perform early exits before password comparison. With Cockroachdb as the backend, the distributed nature of the database and its consistent serialization characteristics do not prevent application-layer timing leaks; they only shape how queries are executed across nodes. If Hanami performs a lookup by username first and then conditionally hashes a password only when the user is found, an attacker can infer valid usernames from marginally faster or slower responses.
Insecure patterns in Hanami might include non-constant-time comparison for credentials or missing lockout mechanisms, and Cockroachdb’s strong consistency can make retries and backoff predictable, aiding an attacker who probes rate limits. Because Cockroachdb serializes transactions, a naive implementation that iterates over login attempts within a transaction can appear to stall or produce consistent error messages, which may amplify information leakage through response times or status codes. The attacker can correlate request latency with whether a username exists or whether account lockout counters are updated, turning what looks like a generic SQL store into an observable side channel when combined with Hanami’s explicit SQL-building patterns.
For example, a route like POST /session that loads a user by email and verifies password without uniform timing or rate limiting can be probed systematically. The Cockroachdb driver in Hanami uses prepared statements and explicit transaction blocks; if the application chooses to run SELECT followed by conditional INSERT or UPDATE for lockout logic, these operations are visible to scanning tools like middleBrick, which flags authentication and BOLA/IDOR risks. middleBrick’s authentication checks can surface missing protections such as lack of account lockout, missing captcha, or inconsistent response behavior, aligning with OWASP API Top 10 and indicating that brute force risk is elevated when authentication controls are not enforced uniformly regardless of backend choice.
Cockroachdb-Specific Remediation in Hanami — concrete code fixes
To harden Hanami against brute force when using Cockroachdb, enforce constant-time operations and rate limiting at the application and infrastructure level. Use a fixed-duration comparison for credentials and ensure authentication paths take the same time regardless of user existence. Implement account lockout or progressive delays tied to a per-identifier scope rather than per-username alone, and avoid branching logic that reveals user presence through timing or status codes.
In your Hanami app, configure Rack::Attack (or a similar middleware) to throttle login attempts by IP or by a hashed identifier, and ensure that responses for failure cases are uniform. Below is a Hanami controller example that uses a parameterized query with the pg driver for Cockroachdb, performs a constant-time check, and returns a generic response:
require "hanami/controller"
require "pg" # Cockroachdb compatible driver
module Web::Controllers::Sessions
class Create
include Hanami::Action
def call(params)
email = params[:email].to_s.strip
input_password = params[:password].to_s
# Use a single query with parameterized inputs; avoid early user lookup branching
sql = <<~SQL
SELECT id, password_hash, locked_until,
(COUNT(*) FILTER (WHERE attempted >= now() - interval '15 minutes')) >= 5 AS is_locked
FROM auth_attempts
WHERE email = $1
GROUP BY id, password_hash, locked_until;
SQL
db = PG.connect(ENV["DATABASE_URL"])
result = db.exec_params(sql, [email])
if result.ntuples == 1
row = result[0]
locked_until = row["locked_until"]
if locked_until && Time.parse(locked_until) > Time.now
halt 401, { error: "Unauthorized" }.to_json
end
# Constant-time comparison to avoid timing leaks
stored_hash = row["password_hash"]
match = ActiveSupport::SecurityUtils.secure_compare(
BCrypt::Password.create(input_password, cost: 12).hash,
stored_hash
)
if match
# Reset attempts on success
db.exec_params("UPDATE auth_attempts SET count = 0, attempted = NULL WHERE email = $1", [email])
# issue session...
else
# Record failed attempt idempotently
db.exec_params(
"INSERT INTO auth_attempts (email, attempted) VALUES ($1, now()) " +
"ON CONFLICT (email) DO UPDATE SET count = auth_attempts.count + 1, attempted = now()",
[email]
)
halt 401, { error: "Unauthorized" }.to_json
end
else
# Even when user not found, perform a dummy hash to keep timing consistent
BCrypt::Password.create(input_password, cost: 12)
halt 401, { error: "Unauthorized" }.to_json
end
end
end
end
On the Cockroachdb schema side, define tables with appropriate constraints to support idempotent upserts and avoid race conditions in distributed deployments:
CREATE TABLE IF NOT EXISTS auth_attempts (
email STRING PRIMARY KEY,
count INT NOT NULL DEFAULT 0,
attempted TIMESTAMPTZ,
locked_until TIMESTAMPTZ
);
For comprehensive protection, combine these coding practices with middleBrick scans that validate the effectiveness of your controls. The CLI can be run locally with middlebrick scan <url> to verify that authentication endpoints are not leaking information, while the GitHub Action can enforce a maximum risk score in CI/CD and the MCP Server can integrate checks into AI-assisted development. The Pro plan adds continuous monitoring to detect regressions, and findings from these scans map to frameworks such as OWASP API Top 10 and PCI-DSS, helping you prioritize fixes.