Broken Authentication in Hanami with Mutual Tls
Broken Authentication in Hanami with Mutual Tls — how this specific combination creates or exposes the vulnerability
Hanami is a Ruby web framework that encourages explicit, layered security. When mutual Transport Layer Security (mTLS) is used, both the client and the server present certificates during the TLS handshake. In theory, mTLS strengthens authentication by binding identity to cryptographic material. However, a misconfiguration or incomplete implementation can break authentication rather than improve it.
One common pattern is to terminate TLS at a load balancer or reverse proxy and forward requests to Hanami over plain HTTP internally. If the application then relies solely on the presence of a client certificate in the request headers (e.g., X-SSL-Client-Cert) without validating the certificate chain, issuer, or revocation status, an attacker can bypass authentication by omitting or spoofing that header. This is a classic broken authentication issue: the identity conferred by mTLS is not being verified by the application, so trust is placed in client-supplied data.
Additionally, Hanami applications may implement session-based authentication on top of mTLS (for example, to manage user sessions after the TLS client certificate is verified). If session tokens or cookies are not marked as Secure and HttpOnly, or if they lack proper SameSite attributes, an attacker who can intercept or steal a session identifier can impersonate a user even when mTLS is in place. This is a broken authentication weakness in how the application manages post-handshake identity.
Another scenario is weak or missing authorization checks after authentication. An attacker authenticated with a valid client certificate might be able to access endpoints or data belonging to other users (BOLA/IDOR). For example, an endpoint like GET /api/v1/users/:id that only checks that a certificate is present, but not whether the authenticated subject matches the requested user ID, exposes a broken authorization flaw enabled by mTLS context.
Finally, if Hanami uses the client certificate for authentication but does not enforce strong cipher suites or disable deprecated TLS versions (e.g., TLS 1.0 or 1.1), the security of the mTLS channel itself is weakened. This can expose authentication credentials or session material to downgrade attacks. Proper configuration of the TLS stack is essential to ensure that mTLS contributes to, rather than undermines, authentication security.
Mutual Tls-Specific Remediation in Hanami — concrete code fixes
Remediation focuses on ensuring that mTLS is correctly integrated with Hanami’s request lifecycle and that authentication decisions are based on verified certificate data, not headers alone.
First, validate the client certificate within the application or at the proxy layer and map it to a trusted identity. If using a Rack middleware approach, inspect the verified peer certificate from the connection environment. Here is an example of a Hanami-compatible Rack middleware that checks the certificate subject and issuer:
require 'openssl'
class MutualTlsAuth
def initialize(app, cert_store_path)
@app = app
@cert_store = OpenSSL::X509::Store.new
@cert_store.add_file cert_store_path
end
def call(env)
client_cert = env['ssl_client_cert']
unless client_cert
return [401, { 'Content-Type' => 'application/json' }, [{ error: 'client certificate required' }.to_json]]
end
store = @cert_store
unless store.verify(client_cert)
return [403, { 'Content-Type' => 'application/json' }, [{ error: 'invalid certificate' }.to_json]]
end
# Optionally check subject or extended key usage
subject = client_cert.subject.to_s
unless subject.match?(/CN=([\w-]+)/)
return [403, { 'Content-Type' => 'application/json' }, [{ error: 'certificate subject invalid' }.to_json]]
end
env['warden'].set_user({ subject: subject, cert_serial: client_cert.serial }, scope: :user)
@app.call(env)
end
end
# config/initializers/mutual_tls.rb
use MutualTlsAuth, '/path/to/ca-bundle.pem'
Second, ensure Hanami’s session cookies are hardened when mTLS is used. Even with client certificates, treat the session as a separate authentication factor and protect it:
# config/initializers/session_store.rb
Hanami.configure do
session do
config.store = :cookie
config.cookie_name = '_hanami_session'
config.secure = true
config.http_only = true
config.same_site = :lax
config.expires = 30 * 24 * 60 * 60 # 30 days
end
end
Third, enforce authorization checks that align with the authenticated identity from the certificate. For example, in a Hanami controller, resolve the user from the certificate subject and verify ownership before acting:
# app/controllers/api/users_controller.rb
class Api::UsersController < Hanami::Controller
before_action :authenticate_by_cert
def show
user = UserRepository.new.find(params[:id])
if user && user.subject == current_user.subject
Response::Json.new(user.to_json)
else
Response::Json.new({ error: 'not found' }, status: 404)
end
end
private
def authenticate_by_cert
subject = request.env['warden'].user[:subject]
halt 401, { error: 'unauthorized' }.to_json unless subject
@current_user = UserRepository.new.find_by_subject(subject)
halt 403, { error: 'forbidden' }.to_json unless @current_user
end
end
Fourth, configure your TLS termination point (proxy or load balancer) to require and verify client certificates and to forward only verified certificate details to Hanami. For example, with a proxy that supports mTLS, ensure that verify_client is enabled and that proxy_set_header does not override the verified identity with untrusted headers.
Lastly, rotate certificates and monitor revocation. Integrate certificate revocation checks (CRL or OCSP) where possible and establish a process to revoke compromised client certificates. Combined with strict session cookie policies and explicit authorization checks, this ensures that mTLS strengthens rather than weakens authentication in Hanami.
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 |