Clickjacking in Hanami with Hmac Signatures
Clickjacking in Hanami with Hmac Signatures — 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 overlaying page. In Hanami, relying only on Hmac Signatures for request verification does not prevent clickjacking because the signature is typically computed over parameters and a timestamp, not over the rendered frame context or the presence of framing headers. If a Hanami endpoint renders HTML intended for embedding but does not set X-Frame-Options or Content-Security-Policy with frame-ancestors, an attacker can embed the page in an <iframe> and overlay transparent controls to capture clicks.
Hmac Signatures in Hanami are commonly used to ensure request integrity—for example, to validate webhook payloads or signed query parameters. However, if the signature is included in a query string or header without protection against being read and reused in a forged framed request, clickjacking can still occur. An attacker can load the legitimate Hanami page in a frame, read the visible UI (if same-origin or via other leaks), and replay the signed request by tricking the user into clicking through the invisible frame. Because the Hmac Signature does not inherently bind the request to a specific frame context or referrer, the server may accept it as valid even though the user did not intend the interaction.
For example, a Hanami webhook endpoint that verifies an Hmac Signature on the payload will still process the request if the attacker can cause the user’s browser to submit a valid signed request via a forged form inside an iframe. The signature may be intact, but the lack of anti-clickjacking headers means the user interface was socially engineered rather than intentionally activated. This is especially relevant when the signed request performs state-changing operations, such as updating settings or initiating transactions. Therefore, Hmac Signatures must be complemented by frame protection mechanisms to mitigate clickjacking in Hanami.
A realistic Hanami controller that uses Hmac Signatures for webhook validation but omits frame protections might look like this:
module Webhooks
class PaymentsController < Hanami::Action
def create
payload = request.body.read
signature = request.headers['X-Signature']
expected = generate_hmac(payload)
if Rack::Utils.secure_compare(expected, signature)
# Process payment — vulnerable if embedded in a clickjacked frame
render json: { status: 'ok' }
else
render json: { error: 'invalid signature' }, status: 401
end
end
private
def generate_hmac(data, key = ENV['WEBHOOK_SECRET'])
OpenSSL::HMAC.hexdigest('sha256', key, data)
end
end
end
Hmac Signatures-Specific Remediation in Hanami — concrete code fixes
To remediate clickjacking risks when using Hmac Signatures in Hanami, you should combine cryptographic integrity checks with anti-framing defenses. Hmac Signatures ensure the request has not been tampered with, but they do not prevent the page from being embedded. Add HTTP headers to restrict framing and ensure that signed endpoints are not usable in an unintended context.
First, configure your Hanami application to set X-Frame-Options and a strict Content Security Policy with frame-ancestors. This prevents browsers from rendering your pages inside iframes unless explicitly allowed. For public-facing endpoints that do not need to be framed, this is a strong mitigation.
Second, when validating Hmac Signatures, consider including additional context such as a nonce or referrer check if your use case requires higher assurance. Below is a concrete Hanami controller example that combines Hmac verification with frame protection headers and a basic origin check to reduce clickjacking risk:
module Webhooks
class PaymentsController < Hanami::Action
def create
# Anti-clickjacking headers
response.headers['X-Frame-Options'] = 'DENY'
response.headers['Content-Security-Policy'] = "frame-ancestors 'none'"
payload = request.body.read
signature = request.headers['X-Signature']
expected = generate_hmac(payload)
if Rack::Utils.secure_compare(expected, signature)
# Optionally verify origin for additional safety
unless allowed_origin?(request.env['HTTP_ORIGIN'])
raise Hanami::Action::UnauthorizedError, 'Origin not allowed'
end
render json: { status: 'ok' }
else
render json: { error: 'invalid signature' }, status: 401
end
end
private
def generate_hmac(data, key = ENV['WEBHOOK_SECRET'])
OpenSSL::HMAC.hexdigest('sha256', key, data)
end
def allowed_origin?(origin)
return false if origin.nil?
# Example: allow only your frontend origin
origin == 'https://app.yourapp.com'
end
end
end
For non-webhook endpoints that render HTML, ensure that layout or application-wide headers enforce frame restrictions. You can use Hanami's middleware configuration to set these headers globally, reducing the chance that any page protected by Hmac Signatures is embedded maliciously. Remember that Hmac Signatures protect integrity and authenticity, but clickjacking defense requires explicit framing controls.