HIGH clickjackinghanamimutual tls

Clickjacking in Hanami with Mutual Tls

Clickjacking in Hanami with Mutual Tls — how this specific combination creates or exposes the vulnerability

Clickjacking is a client-side UI redress attack where an attacker tricks a user into interacting with a hidden or disguised element inside an invisible or misaligned frame. Hanami, a Ruby web framework, does not set default anti-clickjacking protections such as X-Frame-Options or Content-Security-Policy: frame-ancestors, so responses can be embedded by external pages unless explicitly guarded. Mutual Transport Layer Security (Mutual TLS), which requires both the client and the server to present valid certificates during the TLS handshake, is typically used to authenticate clients and enforce strict channel security. While Mutual TLS ensures that only authorized clients can establish a connection and that traffic is encrypted, it does not prevent the server’s responses from being framed by an attacker in a different origin. In other words, Mutual TLS secures the channel and access control at the network layer, but it does not enforce UI-level constraints. A common misconfiguration is to assume that strong transport security alone prevents clickjacking. This assumption can expose Hanami applications when they are served behind a reverse proxy or load balancer that terminates Mutual TLS and forwards requests to the Hanami app over plain HTTP internally. If the Hanami app does not generate frame-prevention headers, the UI remains embeddable regardless of how strong the Mutual TLS binding is. Another specific scenario involves embedded dashboards or administrative panels that are accessible to authenticated users. If these pages are reachable from an origin that also serves public content, an attacker can craft a page that loads the Hanami endpoint in a tiny, layered iframe, aligning UI elements (like buttons or forms) beneath the attacker’s visible controls. Because Mutual TLS does not restrict framing, the browser will render the embedded content and process user interactions as intended by the attacker. MiddleBrick’s unauthenticated scan checks whether frame-prevention headers and CSP frame-ancestors are present; without these controls, the UI surface remains vulnerable even when Mutual TLS is enforced. Developers might also inadvertently weaken protections by allowing cross-origin resource inclusion or by embedding pages in iframes intentionally without considering the broader attack surface. This combination of a UI-focused flaw and a transport-layer control illustrates why layered defenses are essential. Relying solely on Mutual TLS leaves clickjacking risks unaddressed in Hanami, and security testing should include header validation and CSP configuration as part of the 12 parallel checks.

Mutual Tls-Specific Remediation in Hanami — concrete code fixes

Remediation for clickjacking in Hanami must focus on HTTP headers and CSP, independent of Mutual TLS configuration. The primary defenses are X-Frame-Options and Content-Security-Policy with frame-ancestors. Below are concrete, working examples of how to enforce these headers in a Hanami application, alongside a sample Mutual TLS setup for the server.

Hanami middleware to set security headers

In a Hanami application, you can add a middleware that injects security headers for every response. Create a file such as lib/my_app/middleware/security_headers.rb:

module MyApp
  module Middleware
    class SecurityHeaders
      def initialize(app)
        @app = app
      end

      def call(env)
        status, headers, body = @app.call(env)
        headers['X-Frame-Options'] = 'DENY'
        headers['Content-Security-Policy'] = "frame-ancestors 'none';"
        [status, headers, body]
      end
    end
  end
end

Then insert this middleware into the pipeline in config/application.rb:

module MyApp
  class Application < Hanami::Application
    use MyApp::Middleware::SecurityHeaders
    # other middleware and configuration
  end
end

Alternatively, if you prefer to configure headers per route or resource, you can use a before action in your controllers:

class Web::Controllers::Dashboards
  include Web::Action

  before do
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['Content-Security-Policy'] = "frame-ancestors 'none';"
  end

  def show
    # action logic
  end
end

Mutual TLS server configuration example

Mutual TLS must be configured at the web server or reverse proxy layer that terminates TLS. Below is a representative snippet for a typical Ruby-compatible server (e.g., Puma with SSL) or a load balancer configuration that requires client certificates. This is not Hanami-specific but shows how to enforce client certificate verification.

# Example Puma config (config/puma.rb)
ssl_bind '0.0.0.0', '8443', {
  cert:  'path/to/server.crt',
  key:   'path/to/server.key',
  verify_mode: 'verify_peer',
  ca_file: 'path/to/ca_bundle.crt'
}

The verify_mode: 'verify_peer' ensures that clients must present a valid certificate signed by the trusted CA defined in ca_bundle.crt. This enforces Mutual TLS at the transport layer. Remember that after Mutual TLS termination, internal communication to Hanami may be over HTTP, so the security headers must be present on the Hanami responses to protect against clickjacking regardless of the transport security.

Complementary checks

When testing with tools like MiddleBrick, verify that both the security headers and Mutual TLS are in place. MiddleBrick’s scan will flag missing frame-ancestors or X-Frame-Options and will also surface unauthenticated LLM endpoints or other findings. Even with Mutual TLS, missing headers should be prioritized for remediation.

Frequently Asked Questions

Does Mutual TLS prevent clickjacking in Hanami?
No. Mutual TLS secures the TLS channel and client authentication, but it does not prevent HTTP responses from being embedded in frames. You must still set X-Frame-Options or Content-Security-Policy frame-ancestors to prevent clickjacking.
Can I rely on reverse proxy headers instead of Hanemi-level protections?
You can set headers at the proxy, but adding them in Hanami ensures they are present even if requests bypass the proxy. Defense in depth is recommended; use both proxy and application-level header enforcement.