Clickjacking in Hanami (Ruby)
Clickjacking in Hanami with Ruby — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side injection flaw that manipulates a user into interacting with a hidden interface element inside an iframe. In Hanami, a Ruby web framework, this typically arises when views do not enforce a same-origin policy via X-Frame-Options or Content-Security-Policy (CSP) frame-ancestors, and the application exposes sensitive actions (such as account updates or token-bearing POSTs) without additional anti-CSRF considerations. Ruby’s default templating (e.g., Tilt/ERB) does not automatically add frame-busting headers, so the responsibility falls on the developer to set them explicitly.
When a Hanami app renders pages that include forms or links intended for direct interaction, and those pages are served without restrictive framing headers, an attacker can embed the target URL inside an invisible iframe on a malicious site. Because Hanami routes are mapped to Ruby classes with conventional action methods (e.g., Posts::Create), the request lifecycle processes the forged request as a legitimate user action if the session cookie is present and no same-site or anti-CSRF protections are enforced. This combination—Ruby-based server-side rendering in Hanami plus missing CSP frame-ancestors or X-Frame-Options—creates a surface where clickjacking can succeed.
Consider an example where a Hanami Ruby view renders a form to change the user’s email without enforcing strict CSP. An attacker crafts a page with an invisible iframe pointing to /account/update-email and overlays transparent UI elements to trick the user into clicking the visible button, which actually submits the hidden form. Because Hanami can rely on cookie-based sessions, the forged request executes in the victim’s authenticated context if the browser sends the session cookie. The Ruby backend processes the request as valid, demonstrating how the framework’s typical patterns can inadvertently enable clickjacking when headers are omitted.
OpenAPI/Swagger analysis (supported in multiple versions with full $ref resolution) can highlight endpoints that lack security headers in runtime tests, but note that middleBrick detects missing framing protections as part of its 12 checks, specifically under Data Exposure and related to improper CSP configuration. This is important because scanning your Hanami endpoints with a tool that performs black-box testing can surface these missing headers before an attacker exploits them.
Ruby-Specific Remediation in Hanami — concrete code fixes
Remediation focuses on setting HTTP response headers and ensuring that forms are protected by anti-CSRF tokens. In Hanami, you can configure default headers in the application or per action, and you should include frame-busting and CSP headers that restrict frame-ancestors. Below are concrete Ruby code examples for Hanami applications.
Setting default headers in Hanami
Use a Rack middleware or Hanami’s built-in configuration to inject security headers for all responses. Here is a Ruby initializer that adds X-Frame-Options and a strict CSP with frame-ancestors 'self':
# config/initializers/security_headers.rb
module SecurityHeaders
class Middleware
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers['X-Frame-Options'] = 'DENY'
headers['Content-Security-Policy'] = "default-src 'self'; frame-ancestors 'self';"
[status, headers, body]
end
end
end
require 'hanami/configuration'
Hanami.configure do |config|
config.middleware.use SecurityHeaders::Middleware
end
This Ruby snippet wraps the Hanami app with a custom middleware that sets headers on every response, ensuring that browsers will not render the page in an iframe unless it originates from the same origin.
Per-action header control in Hanami actions
If you need tighter control, you can set headers directly in a specific Ruby action. The following Hanami action demonstrates how to add headers to a response for a sensitive operation, such as updating account details:
# apps/web/controllers/account/update.rb
module Web::Controllers::Account
class Update
include Web::Action
def call(params)
# business logic for updating account
headers['X-Frame-Options'] = 'DENY'
headers['Content-Security-Policy'] = "default-src 'self'; frame-ancestors 'self';"
# redirect or render as appropriate
end
end
end
Additionally, ensure your forms include anti-CSRF tokens. Hanami provides built-in helpers when using its view components; for example, include the authenticity token in forms:
<%= form_for @account, action: Routes.account_update_path do |f| %>
<%= f.hidden_field :_csrf_token, value: current_session.csrf_token %>
<%= f.input :email %>
<%= f.submit 'Update' %>
<%= end %>
By combining Ruby middleware for headers with per-action customization and proper CSRF token usage, you mitigate clickjacking risks specific to Hanami applications. For ongoing monitoring, consider integrating middleBrick’s scans (via the CLI: middlebrick scan <url>, the GitHub Action for CI/CD, or the MCP Server in your IDE) to validate that these headers are present and effective across deployed environments.