HIGH clickjackinghanamibearer tokens

Clickjacking in Hanami with Bearer Tokens

Clickjacking in Hanami with Bearer Tokens — 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 disguised frame. In Hanami, a Ruby web framework, including a Bearer token in the Authorization header does not protect a page from being embedded in an <iframe> or <object> on a malicious site. The token is sent automatically by the browser with any same-site credentials, so a privileged action protected only by token presence can be invoked without the user’s consent when the malicious page triggers a form submission or a JavaScript-driven request.

For example, suppose a Hanami app exposes an endpoint that accepts DELETE or POST requests and relies solely on an Authorization: Bearer header for authorization. If the response does not set an appropriate X-Frame-Options or Content-Security-Policy frame-ancestors directive, an attacker can craft a page that embeds the endpoint URL inside a hidden form or image request. When a logged-in user with a valid Bearer token visits the attacker’s page, the browser sends the token with the forged request. Because the token is included automatically, the server may process the action as intended by the legitimate user, leading to unauthorized state changes such as changing email, updating settings, or deleting resources.

To illustrate, consider a Hanami controller that performs a sensitive update:

class Web::Controllers::Account::UpdateEmail
  include Web::Action

  def call(params)
    if auth_header = env['HTTP_AUTHORIZATION']&.then { |h| h.split(' ').last }
      # Verify Bearer token and perform update
      if current_user.update(email: params[:email])
        Response.new.status = 200
      else
        Response.new.status = 422
      end
    else
      Response.new.status = 401
    end
  end
end

If this endpoint does not enforce strict anti-clickjacking protections, an attacker can host a page with:

<!DOCTYPE html>
<html>
<body>
  <form action="https://api.example.com/account/update-email" method="POST" style="display:none;">
    <input type="email" name="email" value="[email protected]" />
    <input type="submit" value="Click Me" />
  </form>
  <script>document.forms[0].submit();</script>
</body>
</html>

Even with a Bearer token, the browser includes the Authorization header if the token was issued for the same domain and the credentials mode permits it. Without explicit anti-framing controls, the server may mistakenly treat the forged request as legitimate. This shows why security in Hanami must include both proper authorization via Bearer tokens and robust UI-layer defenses against embedding.

Bearer Tokens-Specific Remediation in Hanami — concrete code fixes

Remediation in Hanami requires a defense-in-depth approach: enforce anti-framing headers, use secure cookie attributes if sessions are involved, and ensure that sensitive actions require explicit user intent beyond the presence of a Bearer token. Below are concrete, syntactically correct examples tailored to a Hanami application.

1) Set anti-framing headers globally in your application controller to prevent embedding:

class Web::ApplicationController < Hanami::Action
  before do
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['Content-Security-Policy'] = "frame-ancestors 'none'"
  end
end

2) Require additional confirmation for sensitive actions (e.g., email change) by validating a same-site cookie or a per-request CSRF-like token, even when using Bearer authentication. For APIs that are truly token-only and meant for non-browser clients, ensure that sensitive endpoints reject requests that include cookie headers or ambiguous origins:

class Web::Controllers::Account::UpdateEmail
  include Web::Action

  def call(params)
    auth_header = env['HTTP_AUTHORIZATION']&.then { |h| h.split(' ').last }
    return halt(401, { error: 'Unauthorized' }.to_json) unless auth_header && valid_token?(auth_header)

    # Reject requests that include cookie headers in API contexts to reduce confusion-based attacks
    if env['HTTP_COOKIE']
      return halt(400, { error: 'Unexpected credentials' }.to_json)
    end

    # Require a JSON body parameter that is not auto-sent by browsers in cross-origin frames
    email = params[:email]
    return halt(422, { error: 'Missing email' }.to_json) unless email&.match?(URI::MailTo::EMAIL_REGEXP)

    if current_user.update(email: email)
      Response.new.status = 200
    else
      Response.new.status = 422
    end
  end

  private

  def valid_token?(token)
    # Implement your token validation logic (e.g., JWT verification or DB lookup)
    token == 'expected_secure_token_value'
  end
end

3) For browser-based clients that use cookies alongside tokens, separate concerns clearly and avoid relying on cookies for authorization when Bearer tokens are the primary mechanism:

# config/initializers/session_store.rb
Hanami::Web::App.configure do
  use Rack::Session::Cookie, key: '_hanami_session', secure: true, httponly: true, same_site: :strict
end

4) When serving pages that include sensitive actions, explicitly require a same-site cookie or a custom header that cannot be set cross-origin:

class Web::Controllers::Sensitive::FormPage
  include Web::Action

  def call(params)
    # Require a custom header set by your frontend JavaScript running on the trusted origin
    expected = env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
    return halt(403) unless expected

    # Set anti-framing headers for this page
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['Content-Security-Policy'] = "frame-ancestors 'none'"

    # Render your form page...
  end
end

These measures ensure that even if a Bearer token is present, the browser cannot silently trigger unauthorized actions from a malicious site, mitigating clickjacking risks specific to token-based authentication in Hanami.

Frequently Asked Questions

Does setting X-Frame-Options or CSP prevent Bearer token theft?
No. These headers prevent the page from being embedded but do not stop an attacker from stealing the Bearer token via other means such as XSS or network interception.
Should I include Bearer tokens in URLs to prevent clickjacking?
No. Including tokens in URLs increases exposure via logs, browser history, and Referer headers; use headers instead and ensure proper anti-framing protections.