Clickjacking in Hanami with Api Keys
Clickjacking in Hanami with Api Keys — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redress attack where an attacker tricks a user into clicking or interacting with a hidden or disguised element within a transparent or opaque overlay. In a Hanami application that exposes API keys in responses or UI flows, the combination of clickjacking and leaked keys can lead to unauthorized actions and key exfiltration. Hanami, being a Ruby web framework that encourages explicit views and minimal magic, does not automatically protect against frame embedding unless developers opt in with modern headers.
When API keys are returned in HTML responses, embedded in JavaScript variables, or rendered in forms without anti-clickjacking protections, an attacker can craft a page that loads the Hanami app inside an <iframe> and overlay invisible controls to capture key disclosures or trigger key-using endpoints. For example, an endpoint that returns a JSON representation of a key in a browser (e.g., for debugging or developer experience) might be invoked via a form or link that an attacker can mask with a transparent layer. If the response includes keys in plaintext and lacks frame-busting or Content-Security-Policy frame-ancestors rules, the key can be exposed to a logged-in user who is tricked into interacting with the invisible overlay.
Moreover, if the Hanami app embeds API keys in frontend JavaScript (e.g., for client-side libraries or service integrations), clickjacking can be used to drive the user to perform actions that send those keys to attacker-controlled endpoints. The risk is amplified when API keys are used for third-party integrations and the app lacks strict referrer and CSP policies. OWASP Top 10 A05:2021 (Security Misconfiguration) and A02:2021 (Cryptographic Failures) are relevant when keys are exposed in responses without adequate transport protections or frame-ancestor restrictions. middleBrick scans for such exposures under Data Exposure and Input Validation checks, flagging endpoints that leak secrets in unauthenticated contexts.
Api Keys-Specific Remediation in Hanami — concrete code fixes
Remediation centers on preventing key leakage in responses and hardening the UI against framing. Below are concrete Hanami patterns and code examples you can apply.
1. Prevent key leakage in JSON responses
Ensure API keys are never returned in HTML or JSON responses unless strictly necessary and properly scoped. Use view models or serializers that exclude sensitive fields in non-admin contexts.
# app/views/api_keys/show.json.eye
object.only(*allowed_attributes)
object.except!(:value) if current_user.admin? == false
# Allowed attributes might exclude :value or :raw_key
allowed_attributes = [:id, :name, :created_at] unless current_user.admin?
2. Set HTTP headers to prevent framing
Add middleware or a base controller to enforce X-Frame-Options and Content-Security-Policy frame-ancestors. In Hanami, you can do this in a base action or via a Rack middleware configuration outside of Hanami’s internal concerns.
# config/initializers/frame_protection.rb
Rack::Protection::FrameOptions.default_options[:block] = true
# Alternatively, in a Hanami controller concern
module FrameProtection
def self.included(base)
base.before :set_frame_headers
end
def set_frame_headers
response.headers['X-Frame-Options'] = 'DENY'
response.headers['Content-Security-Policy'] = "frame-ancestors 'none'"
end
end
class ApplicationController < Hanami::Controller
include FrameProtection
end
3. Avoid embedding keys in JavaScript
Do not serialize API keys into HTML or inline scripts. If client-side keys are required, serve them via a protected endpoint with strict CORS and short-lived tokens instead of long-lived API keys.
# BAD: exposes key in HTML source
<script>const apiKey = '<%= ENV['EXTERNAL_API_KEY'] %>'</script>
# BETTER: obtain via an authenticated endpoint that returns a scoped token
fetch('/api/v1/token', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + session_jwt },
body: JSON.stringify({ scope: 'external_service' })
}).then(r => r.json()).then(data => useToken(data.token))
4. Enforce strict Content-Security-Policy
Limit where frames can be embedded from. Use a restrictive CSP in production to mitigate clickjacking even if X-Frame-Options is absent.
# config/initializers/content_security_policy.rb
Hanami::Middleware::ContentSecurityPolicy.configure do |policy|
policy.frame_ancestors none: true
policy.default_src :self, :https
policy.script_src :self, :https, :unsafe_inline # avoid :unsafe_eval
end
5. Validate and sanitize inputs to prevent injection via UI
Ensure that any user-controlled data used in views does not enable injection that could facilitate clickjacking vectors (e.g., open redirects that lead to key-bearing pages).
# app/validators/api_key_input_validator.rb
class ApiKeyInputValidator
include Hanami::Validations::FormValidator
validations do
required(:name).filled(:str?)
required(:value).filled(:str?, format?: /^[A-Za-z0-9\-_]+$/)
end
end