Auth Bypass in Hanami with Hmac Signatures
Auth Bypass in Hanami with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Hanami applications that use Hmac Signatures for request authentication can be vulnerable to authentication bypass when the signature verification logic is incomplete or misaligned with the runtime behavior of the framework. In a typical setup, the client computes an Hmac over selected parts of the request (method, path, selected headers, and body) using a shared secret, and sends the signature in a custom header such as x-api-signature. The server recomputes the Hmac using the same algorithm and secret, then compares the two signatures. If the comparison is performed incorrectly—such as using a non-constant-time comparison, failing to normalize whitespace, or including or excluding elements inconsistently—an attacker can craft inputs that produce the same signature (collision) or exploit timing differences to infer information about the valid signature.
In Hanami, a common pattern is to compute the Hmac over the request payload and a deterministic subset of headers, then verify the signature in a before action or a custom middleware. However, if the application does not explicitly include the HTTP method and the request path in the signed payload, or if it includes query parameters inconsistently between client and server, the signature can be reused across different endpoints or methods. This inconsistency effectively decouples the signature from the intended resource and action, allowing an authenticated-looking request to a different endpoint to be accepted as valid. For example, a signature computed for POST /admin/users without the path may be accepted by the server when presented to POST /admin/roles, provided the body and selected headers match, leading to privilege escalation or unauthorized operations.
Another specific vector arises when Hanami applications parse JSON bodies and then serialize them differently for signature verification (e.g., differing key ordering, inclusion or exclusion of null values, or handling of nested objects). If the server normalizes the payload one way and the client another, a valid signature on one side may not match on the other, prompting developers to disable strict payload checks or to fall back to a more permissive comparison. Such permissiveness can open the door to signature collision attacks or allow an attacker to supply additional parameters that are ignored during verification but alter server behavior, a pattern that overlaps with BOLA/IDOR when object ownership is inferred from the payload rather than from a secure context. The vulnerability is compounded when the Hmac is the only authentication mechanism and there is no binding to a specific scope, such as tenant ID or user ID, within the signed data.
Runtime factors can also exacerbate the issue. For instance, if the shared secret is stored in an environment variable that is accidentally exposed through logs or error messages, or if the secret is rotated infrequently, an attacker who obtains a single valid signature and knows the algorithm can replay it across a window of validity. Because Hanami does not enforce a strict timestamp or nonce by default in such custom signature schemes, replay attacks become feasible, especially if the application does not implement rate limiting or idempotency checks at the endpoint level. The combination of weak canonicalization, missing binding to the request target, and lack of replay protection creates a practical authentication bypass scenario that an attacker can exploit using intercepted traffic or compromised client-side code.
Hmac Signatures-Specific Remediation in Hanami — concrete code fixes
To remediate authentication bypass risks when using Hmac Signatures in Hanami, align the signing and verification logic precisely, bind the signature to the full request context, and use secure comparison patterns. Below is a minimal, secure Hanami service object that computes and verifies an Hmac over the HTTP method, request path, selected headers, and a canonical JSON body. The example uses OpenSSL for Hmac-SHA256 and ActiveSupport::SecurityUtils.secure_compare to prevent timing attacks.
require 'openssl'
require 'active_support/security_utils'
class HmacSignatureService
ALGORITHM = 'sha256'
SECRET = ENV.fetch('API_HMAC_SECRET') { raise 'Missing HMAC secret' }
def self.compute(method, path, headers_to_sign, body)
payload = canonical_payload(method, path, headers_to_sign, body)
OpenSSL::HMAC.hexdigest(ALGORITHM, SECRET, payload)
end
def self.verify(method, path, headers_to_sign, body, received_signature)
computed = compute(method, path, headers_to_sign, body)
ActiveSupport::SecurityUtils.secure_compare(computed, received_signature)
end
private
def self.canonical_headers(headers_to_sign)
headers_to_sign.sort.map { |k, v| "#{k.downcase}:#{v.strip}" }.join('|')
end
def self.canonical_payload(method, path, headers_to_sign, body)
[method.upcase, path, canonical_headers(headers_to_sign), body].join("\n")
end
end
In your Hanami controller or action, ensure the method, path, and a deterministic set of headers are included in the verification. For JSON payloads, canonicalize the body by sorting keys and excluding non-validated fields before verification.
class Api::V1::BaseController < Hanami::Controller
before do
method = request.request_method
path = request.path.to_s
headers = select_headers_for_signing(request.headers)
body = request.body.read
request.body.rewind
signature_header = request.headers['x-api-signature']
unless HmacSignatureService.verify(method, path, headers, body, signature_header)
raise Hanami::Action::UnauthorizedError, 'Invalid signature'
end
end
private
def select_headers_for_signing(headers)
# Be explicit about which headers are included in the Hmac
{ 'content-type' => headers['content-type'], 'x-request-id' => headers['x-request-id'] }
end
end
To prevent replay and binding issues, include a timestamp and nonce in the signed payload if you require replay protection at the Hmac layer, and validate freshness server-side. Also bind the signature to a tenant or user identifier so that signatures cannot be reused across scopes. Avoid including sensitive or mutable fields in the canonical body; instead, use stable identifiers and explicit field selection. Periodically rotate the shared secret and store it securely, and enforce transport-layer protections to reduce the risk of secret leakage. These steps ensure that Hmac Signatures in Hanami provide a robust binding between the request context and the authentication decision.
Related 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 |