MEDIUM clickjackingphoenixhmac signatures

Clickjacking in Phoenix with Hmac Signatures

Clickjacking in Phoenix with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Clickjacking is a client-side injection attack where an attacker tricks a user into clicking or interacting with a hidden or disguised UI element inside an embedded frame. When Phoenix applications embed HTTP responses inside <iframe> or <frame> elements without protection, and also use Hmac Signatures only for request authentication (e.g., to validate webhook or API calls), the two mechanisms can interact in risky ways.

Hmac Signatures in Phoenix are typically computed over selected parts of a request — such as method, path, timestamp, and a shared secret — and passed in a header (e.g., X-API-Signature). If the server uses these signatures to authorize sensitive actions (state-changing POSTs, webhook deliveries, or admin links) but does not enforce strict framing protections, an attacker can construct a malicious page that loads the target endpoint in a hidden frame and relies on the user’s authenticated session to send a signed request. Because the signature is tied to the request parameters and not to the framing context, the server may still validate the Hmac and perform the action, believing it is a legitimate, signed request.

For example, consider a Phoenix endpoint that accepts a payment action via GET with an Hmac-signed token in a query parameter. If this endpoint is also rendered in an iframe by a third-party site, an attacker could trick a logged-in user’s browser into submitting the signed URL inside a form or image request. The server verifies the Hmac, sees valid parameters, and proceeds with the transaction. The vulnerability here is not in Hmac itself, but in the absence of anti-clickjacking controls (such as X-Frame-Options or Content-Security-Policy frame-ancestors) combined with state-changing operations that rely solely on signed URLs without requiring a same-origin context or a per-request nonce tied to the session.

Another scenario involves webhook endpoints that verify an Hmac signature to ensure the payload originates from a trusted source. If the webhook response or confirmation page is inadvertently framed by a malicious site, and the UI reflects sensitive information or provides actionable controls without additional CSRF or frame-busting protections, an attacker might leverage social engineering to induce user interaction inside the frame. Because the Hmac validated the webhook origin, the server may render state-modifying UI components or links that are embedded and activated via user click within the attacker’s page, effectively combining UI redress with trusted webhook validation.

To assess this combination during a scan, middleBrick tests unauthenticated attack surfaces across the 12 checks, including Input Validation and SSRF, and flags missing anti-clickjacking headers alongside endpoints that use Hmac Signatures for sensitive actions. This helps identify where signature-based trust is insufficient without complementary framing protections.

Hmac Signatures-Specific Remediation in Phoenix — concrete code fixes

Remediation focuses on two layers: preventing framing of sensitive endpoints and ensuring Hmac usage includes context that cannot be forged by an attacker in a frame. Below are concrete, working examples in Phoenix/Elixir.

1. Prevent framing with HTTP headers

Ensure sensitive responses include anti-clickjacking headers. In your endpoint or a pipeline, set:

defmodule MyAppWeb.Plugs do
  import Plug.Conn

  def put_frame_options(conn, opts \\ [to: "DENY"]) do
    put_resp_header(conn, "x-frame-options", "DENY")
  end

  def put_csp_frame_ancestors(conn, opts \\ [sources: ["'none'"]]) do
    val = Enum.join(opts[:sources], " ")
    put_resp_header(conn, "content-security-policy", "frame-ancestors #{val}")
  end
end

Use these plugs in your router or pipeline to protect endpoints that perform state changes or render authenticated content.

2. Include nonce or origin in Hmac computation

When generating and verifying Hmac Signatures, include the request origin or a per-request nonce so that a signed payload cannot be reused from an embedded context. Example signature generation and verification:

defmodule MyAppWeb.Hmac do
  @secret System.get_env("HMAC_SECRET")

  def generate(params) when is_map(params) do
    # Include origin and a timestamp to bind the signature to context
    payload = Jason.encode!(%{
      params: params,
      origin: params["origin"],
      ts: System.system_time(:second)
    })
    :crypto.mac(:hmac, :sha256, @secret, payload)
    |> Base.encode16(case: :lower)
  end

  def verify(params, received_sig) do
    expected = generate(params)
    # Use constant-time compare to avoid timing attacks
    :crypto.verify(:hmac, :sha256, @secret, Jason.encode!(params), received_sig)
    |> match?({1, _})
  end
end

# In a controller or webhook handler:
defmodule MyAppWeb.OrderController do
  use MyAppWeb, :controller

  def create(conn, %{"amount" => amount, "origin" => origin, "signature" => sig} = params) do
    # Ensure the origin matches the request origin to prevent framing from other origins
    case MyAppWeb.Hmac.verify(%{"amount" => amount, "origin" => origin}, sig) do
      true ->
        # Additional check: require the origin header to match the request's origin
        if valid_origin?(origin, conn) do
          # Process order safely
          json(conn, %{status: "ok"})
        else
          send_resp(conn, 403, "Invalid origin")
        end
      false ->
        send_resp(conn, 401, "Invalid signature")
    end
  end

  defp valid_origin?(origin, conn) do
    request_origin = Plug.Conn.get_req_header(conn, "origin") |> List.first()
    # Basic same-origin check; tailor to your deployment
    origin == request_origin
  end
end

This approach binds the Hmac to an explicit origin, making it difficult for an attacker to forge a signed request inside a frame unless they can control the origin header (which browsers do not allow to be set cross-origin).

3. Combine with per-request nonce and strict referrer checks

For additional assurance, include a server-generated nonce in signed requests and validate it on the server. Also enforce strict Referrer-Policy to limit referrer leakage.

defmodule MyAppWeb.Plugs do
  import Plug.Conn

  def put_nonce_header(conn) do
    nonce = :crypto.strong_rand_bytes(16) |> Base.encode16()
    conn
    |> put_resp_header("x-content-type-options", "nosniff")
    |> put_resp_header("referrer-policy", "strict-origin-when-cross-origin")
    |> assign(:request_nonce, nonce)
  end
end

# Include the nonce in forms and signed requests:
# 
# And verify it server-side in your Hmac verification step.

Frequently Asked Questions

Can Hmac Signatures alone prevent clickjacking in Phoenix?
No. Hmac Signatures provide request integrity and help verify the source of a request, but they do not prevent a page from being embedded in an iframe. You must add anti-clickjacking headers such as X-Frame-Options or Content-Security-Policy frame-ancestors to prevent framing of sensitive endpoints.
How does including an origin or nonce in the Hmac improve security against clickjacking?
Including the request origin or a per-request nonce binds the signature to a specific context. This means a signed payload generated in an attacker’s page (with a different or missing origin) will fail verification, reducing the risk that a forged request executed inside a frame is accepted by the server.