Api Key Exposure in Hanami with Mutual Tls
Api Key Exposure in Hanami with Mutual Tls — how this specific combination creates or exposes the vulnerability
Hanami is a Ruby web framework that encourages explicit configuration and modular design. When mutual TLS (mTLS) is used to authenticate clients, developers often assume the transport is sufficient and may inadvertently expose API keys through application logic or logging. Api key exposure in this context occurs when an API key intended for downstream service authentication is stored in the request environment, passed to views, written to logs, or included in error messages that traverse the mTLS-secured channel.
With mTLS, the server validates the client certificate, but the application still needs to decide how to use the API key. If the key is extracted from the certificate subject, stored in a global configuration, or echoed in debug output, an attacker who can read logs or trigger error pages over the same mTLS connection may obtain the key. This is especially relevant when mTLS is used for service-to-service calls where the client identity is verified but the application does not apply additional checks on what the client is allowed to do with the key.
During a middleBrick scan of an API deployed in a Hanami environment using mTLS, the following can be observed: the scanner checks whether API keys appear in response bodies, headers, or logs across unauthenticated endpoints, and whether they are unnecessarily echoed in error payloads. Even though mTLS protects against on-path interception, it does not prevent the application from mishandling secrets internally. For example, a Hanami action that renders params[:api_key] in a JSON response or logs it via Hanami.logger.info can leak the key to anyone who can trigger that endpoint and read the output or logs reachable over the authenticated TLS channel.
OWASP API Top 10 A01:2023 broken object level authorization and A05:2023 security misconfiguration are relevant here. Even with mTLS, failing to limit what callers can do with an API key and exposing the key in responses or logs violates the principle of least privilege and data exposure prevention. The scanner’s findings may include ‘Data Exposure’ and ‘Property Authorization’ checks that highlight whether the key is returned or logged, and whether authorization checks are applied consistently across operations that use the key.
In summary, using mTLS in Hanami does not automatically prevent api key exposure; it shifts the risk from network eavesdropping to application-level handling. If the framework configuration or code treats the API key as a trusted, low-sensitivity value, the key can still be disclosed through responses, logs, or error messages that are visible to authenticated mTLS clients.
Mutual Tls-Specific Remediation in Hanami — concrete code fixes
Remediation focuses on ensuring API keys are never returned to the client, logged at inappropriate levels, or used beyond their intended scope, even when mTLS is in place. Below are concrete Hanami examples that demonstrate secure handling.
1. Do not echo API keys in responses
Ensure controller actions do not render API keys. Instead, use a dedicated, minimal representation.
# app/controllers/api/v1/resources/show.rb
module Api
module V1
class ResourcesController < Hanami::Action
def show(ctx)
resource = ResourceRepository.new.find(ctx.params[:id])
# Do NOT include raw_api_key in the response
ctx.response.status = 200
ctx.response.body = MultiJson.dump(id: resource.id, name: resource.name)
end
end
end
end
2. Avoid logging sensitive values
Filter API keys before logging, and avoid passing raw keys to the logger.
# config/initializers/hanami_logger.rb
Hanami.configure do |config|
config.logger = Logger.new($stdout).tap do |logger|
original_formatter = logger.formatter
logger.formatter = ->(severity, time, progname, msg) do
# Redact potential API key values in log messages
msg = msg.to_s.gsub(/api_key=[^&\s]+/, 'api_key=[REDACTED]')
original_formatter.call(severity, time, progname, msg)
end
end
end
3. Use environment-based configuration, not request-time injection
Load API keys from the environment at boot, not from user input, and reference them symbolically in the app.
# config/apps/api.rb
module MyApp
class API < Hanami::Application
configure do |config|
config.api.upstream_key = ENV['UPSTREAM_API_KEY']
end
# Use in a gateway or client setup, not in controller params
finalizer do |_ctx, _env, result|
4. Enforce authorization per operation
Even with mTLS, apply per-action or per-domain authorization to decide whether a caller may use a particular key.
# app/policies/resource_policy.rb
class ResourcePolicy
def initialize(user_certificate_subject, action)
@subject = user_certificate_subject
@action = action
end
def permitted?
# Implement mapping from certificate subject to allowed scopes
allowed_scopes.include?(@action)
end
private
def allowed_scopes
case @subject
when /CN=service-a/ then %w[read write]
when /CN=service-b/ then %w[read]
else []
end
end
end
# app/controllers/api/v1/resources/update.rb
module Api
module V1
class ResourcesController < Hanami::Action
def update(ctx)
policy = ResourcePolicy.new(ctx.client_cert_subject, 'write')
unless policy.permitted?
ctx.response.status = 403
ctx.response.body = MultiJson.dump(error: 'forbidden')
return
end
# Proceed with update using the configured key, not a user-supplied one
# ...
end
end
end
end
5. Validate certificate fields and do not derive secrets from them
Do not use certificate fields as secrets. Keep API keys in environment variables and treat mTLS strictly for identity, not as a bearer token replacement.