Bleichenbacher Attack in Hanami with Mutual Tls
Bleichenbacher Attack in Hanami with Mutual Tls — how this specific combination creates or exposes the vulnerability
A Bleichenbacher attack targets RSA-based cipher suites by exploiting error differences in PKCS#1 v1.5 padding validation. In Hanami, when Mutual TLS is used, the server presents a certificate and expects the client to authenticate with its own cert. Even with client certificates, the application may still accept encrypted parameters (e.g., a token or encrypted payload) that are decrypted using RSA. If the decryption routine returns distinct timing or error messages for valid versus invalid padding, an attacker can iteratively adaptively query the service, sending modified ciphertexts and observing responses to gradually decrypt data without the private key.
With Mutual TLS enabled, the attacker must present a valid client certificate to reach the endpoint, but once authenticated, the server’s handling of encrypted data remains the weak link. Hanami applications that use RSA decryption for session tokens, API keys, or JWTs over TLS connections can inadvertently expose a Bleichenbacher oracle. The combination of Mutual TLS (which ensures identity) and a padding oracle in the application logic means an authenticated attacker can systematically recover plaintext by observing whether decrypted values produce valid padding, leading to sensitive data exposure such as session identifiers or secrets.
For example, an endpoint that accepts an encrypted cookie encrypted_session and decrypts it using RSA may return HTTP 400 for bad padding and HTTP 200 for good padding. An attacker with a valid client certificate can perform the adaptive chosen-ciphertext process described by Daniel Bleichenbacher, sending thousands of modified ciphertexts to infer the plaintext. This violates the principle that error handling should be uniform and timing-independent, and it maps to OWASP API Top 10:2023 Broken Object Level Authorization and Security Misconfiguration.
middleBrick detects this risk by analyzing OpenAPI specs and runtime behavior for RSA decryption patterns and non-uniform error responses. Findings include severity Medium with remediation guidance to replace RSA with authenticated encryption, use constant-time comparisons, and ensure errors are generic.
Mutual Tls-Specific Remediation in Hanami — concrete code fixes
To mitigate Bleichenbacher-style padding oracle issues in Hanami while using Mutual TLS, ensure decryption routines run in constant time and avoid exposing padding errors. Use authenticated encryption (e.g., AES-GCM) instead of raw RSA decryption for sensitive data. If RSA is required, apply RSA-OAEP with strict error handling and uniform responses.
Hanami example with Mutual TLS and safe decryption
# Gemfile
gem 'hanami'
gem 'hanami-router'
gem 'hanami-action'
gem 'openssl'
# config/application.rb
module MyApp
class Application < Hanami::Application
configure do |config|
config.middleware.insert_after ActionDispatch::SSL, Rack::SSL if config.ssl
end
end
end
# app/actions/sessions.rb
require 'openssl'
require 'base64'
class Sessions::Create
def initialize(certificate_store: nil)
@certificate_store = certificate_store || OpenSSL::X509::Store.new
end
def call(env)
request = Hanami::Request.new(env)
client_cert = request.client_cert # from Mutual TLS
encrypted_blob = request.params['token']
if client_cert.nil? || !valid_client_cert?(client_cert)
return [401, { 'Content-Type' => 'application/json' }, [{ error: 'unauthorized' }.to_json]]
end
begin
plaintext = safe_constant_time_decrypt(encrypted_blob)
# proceed with authenticated session setup
[200, { 'Content-Type' => 'application/json' }, [{ data: plaintext }.to_json]]
rescue SecurityError => e
# Always return the same generic error and status
[400, { 'Content-Type' => 'application/json' }, [{ error: 'invalid_request' }.to_json]]
end
end
private
def valid_client_cert?(cert)
# Validate against your CA store; keep this check fast and constant-time where possible
@certificate_store.verify(cert)
end
# Use RSA-OAEP with no distinguishable errors; avoid raw PKCS1v15
def safe_constant_time_decrypt(blob)
key = load_server_key # PEM bytes, protected
decipher = OpenSSL::PKey::RSA.new(key)
# OAEP padding; raises OpenSSL::PKey::RSAError on any failure
decipher.private_decrypt(Base64.strict_decode64(blob), OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
rescue OpenSSL::PKey::RSAError
raise SecurityError, 'decryption failed'
end
end
# app/routes.rb
Routable.routes do
post '/sessions', to: 'sessions#create'
end
Key points:
- Mutual TLS is enforced by checking
request.client_certbefore processing the token. - Decryption uses RSA-OAEP instead of PKCS#1 v1.5, and errors are caught and mapped to a generic 400 response to remove padding oracle signals.
- No timing-dependent branches on padding validity; the operation either succeeds or raises a uniform exception.
- middleBrick can scan this Hanami app to verify that decryption patterns do not expose distinguishable errors and that Mutual TLS is properly integrated.
middleBrick integration
Use the CLI to scan your Hanami endpoint: middlebrick scan https://api.yourapp.com. The GitHub Action can enforce a score threshold, and the MCP Server allows you to scan APIs directly from your IDE while developing these fixes.