Clickjacking in Phoenix with Api Keys
Clickjacking in Phoenix with Api Keys — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redressing attack where an attacker tricks a user into clicking or interacting with a hidden or disguised element. In a Phoenix application that exposes API keys in frontend JavaScript or through predictable endpoints, this interaction can lead to unauthorized actions or key exfiltration. When API keys are embedded in JavaScript bundles or rendered in the DOM, an invisible iframe or overlay can capture user clicks intended for legitimate UI controls and route them to endpoints that use those keys, effectively performing actions on behalf of the user without consent.
Phoenix applications often integrate with external services using API keys passed via HTTP headers or query parameters. If these keys are used in client-side requests and the application lacks frame-busting defenses or Content Security Policy (CSP) directives, an attacker can load the vulnerable page in a malicious site and overlay interactive elements. For example, an attacker might load https://example.com/dashboard in an iframe, position a transparent button over a "Revoke All Keys" or "Rotate Key" action, and rely on user interaction to trigger state changes. Because Phoenix templates render dynamic content and API integrations often rely on static keys stored in configuration or JavaScript, this creates a pathway for unauthorized operations driven by user clicks.
The risk is compounded when API keys are used for authentication rather than session-based tokens. Unlike session cookies, API keys are typically static and do not automatically rotate. If an attacker can induce a logged-in user to click a compromised page, requests originating from that browser will carry the user’s authorization context and any exposed keys. MiddleBrick’s scans detect scenarios where API keys appear in client-rendered code or where endpoints lack anti-CSRF protections, flagging these combinations as high-risk for clickjacking-assisted abuse.
Real-world attack patterns mirror OWASP’s Top 10 for API Security, particularly A05:2023 – Security Misconfiguration and A01:2023 – Broken Access Control. When API keys are not scoped to server-side usage only, and endpoints do not validate the request origin, the attack surface expands. MiddleBrick’s checks for Input Validation and Property Authorization help identify whether requests originating from the client can trigger sensitive operations using static keys, highlighting misconfigurations that enable clickjacking-assisted exploitation.
Api Keys-Specific Remediation in Phoenix — concrete code fixes
Remediation focuses on preventing API keys from being usable in a clickjacking context by ensuring they are never exposed to the client and by enforcing strict request validation. The first step is to avoid embedding API keys in JavaScript or rendering them in templates. Instead, keys should remain server-side and be injected into requests from Phoenix controllers or context modules. Below is a secure example of using API keys in a Phoenix controller without exposing them to the client.
defmodule MyAppWeb.ApiController do
use MyAppWeb, :controller
@external_resource "${System.get_env("API_KEY_PATH")}"
@api_key System.get_env("EXTERNAL_API_KEY")
def fetch_external_data(conn, _params) do
headers = [{"Authorization", "Bearer #{@api_key}"}]
url = "https://external-service.example.com/data"
case HTTPoison.get(url, headers) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
json(conn, Jason.decode!(body))
{:error, %HTTPoison.Error{reason: reason}} ->
conn
|> put_status(:bad_gateway)
|> json(%{error: "External service unavailable", reason: reason})
end
end
end
This pattern ensures the key exists only in server memory and is never serialized into HTML or JavaScript. Environment variables are loaded at runtime, and the key is used strictly within server-to-server communication. To further mitigate clickjacking risks, enforce a strict CSP header that prevents inline scripts and external frame embedding.
In templates, avoid any dynamic rendering of keys or endpoints that could be influenced by user input. If client-side requests are necessary, use short-lived, user-specific tokens issued by Phoenix endpoints rather than static API keys. Below is an example of how to issue a scoped token via a Phoenix endpoint that proxies external requests without exposing long-lived keys.
defmodule MyAppWeb.ProxyController do
use MyAppWeb, :controller
def proxy_call(conn, %{"action" => action}) do
user_token = Guardian.encode_and_sign(%{sub: conn.assigns.current_user.id}, %{})
external_url = "https://external-service.example.com/#{action}"
headers = [
{"Authorization", "Bearer #{user_token}"},
{"X-Requested-With", "Phoenix"}
]
case HTTPoison.post(external_url, %{}, headers) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
json(conn, Jason.decode!(body))
_ ->
send_resp(conn, 502, "Bad gateway")
end
end
end
Additionally, include HTTP headers that defend against clickjacking across all responses. In your endpoint or router, set the X-Frame-Options and Content-Security-Policy headers to restrict framing and inline script execution.
defmodule MyAppWeb.Plugs.Csp do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
put_resp_header(conn, "content-security-policy",
"default-src 'self'; frame-ancestors 'none'; script-src 'self' 'sha256-...'"
)
|> put_resp_header("x-frame-options", "DENY")
end
end
Ensure this plug is added to your pipeline so that every response explicitly denies being framed. MiddleBrick’s scans include checks for missing CSP and X-Frame-Options headers, helping you identify endpoints that remain vulnerable to clickjacking when API keys are involved.