Clickjacking in Fastapi with Basic Auth
Clickjacking in Fastapi with Basic Auth — 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 transparent or opaque element embedded over a legitimate page. In a Fastapi application that uses HTTP Basic Authentication, combining browser-based login flows with pages that do not enforce frame isolation can expose authentication actions to clickjacking. When Basic Auth is handled by the browser (rather than a custom login form), the browser’s built-in credentials dialog is not vulnerable to DOM overlay attacks; however, if the application renders pages that can be framed, an attacker can overlay invisible frames or buttons on top of legitimate UI elements (such as “Confirm” or “Revoke” actions) and capture user interactions, including initiating authenticated flows that trigger the browser’s Basic Auth prompt.
Consider a Fastapi endpoint that conditionally prompts for Basic Auth by returning a 401 with WWW-Authenticate: Basic. If the same endpoint serves HTML that is embeddable in an <iframe> (missing X-Frame-Options or Content-Security-Policy: frame-ancestors), an attacker can embed the endpoint in a malicious page. When the victim visits the attacker’s site, the browser may show the native credentials dialog if no cached credentials exist. Even if credentials are cached and the request proceeds inside the invisible frame, the attacker can drive UI interactions (e.g., form submissions or API calls initiated from the embedded origin) that occur within the authenticated session opened inside the frame. Because the attacker controls the framing context, they can induce the victim to perform actions while the browser attaches the Basic Auth credentials automatically, leading to unauthorized operations like changing settings or making requests on behalf of the user.
middleBrick detects this risk by running the 12 parallel security checks, including Input Validation and Security Headers. For an endpoint served by Fastapi that lacks frame-ancestors restrictions or relies on browser-managed authentication in an embeddable context, the scan reports a finding with severity Medium and references the OWASP UI Redress category. The scan does not assume an authenticated session; it observes whether the response permits framing and whether authentication is browser-driven in a way that can be abused via clickjacking vectors. Remediation focuses on preventing the page from being embedded and ensuring that sensitive actions require explicit user consent outside of easily frameable contexts.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
To mitigate clickjacking in a Fastapi application using HTTP Basic Authentication, you must enforce strict frame control and avoid relying on the browser’s default authentication dialog inside frames. The primary defenses are security headers and careful handling of authentication flows.
Security headers in Fastapi
Set X-Frame-Options to DENY or SAMEORIGIN and use Content-Security-Policy with a restrictive frame-ancestors directive. These headers prevent the browser from rendering the page inside an iframe unless explicitly allowed.
from fastapi import Fastapi, Request
from fastapi.responses import HTMLResponse
app = Fastapi()
@app.middleware("http")
async def add_security_headers(request: Request, call_next):
response = call_next(request)
if not response.headers.get("X-Frame-Options"):
response.headers["X-Frame-Options"] = "DENY"
if not response.headers.get("Content-Security-Policy"):
response.headers["Content-Security-Policy"] = "frame-ancestors 'none'"
return response
This ensures that pages requiring authentication cannot be embedded, reducing the attack surface for clickjacking when using browser-managed Basic Auth.
Explicit authentication flow
Instead of relying on the browser’s Basic Auth dialog inside frames, implement a form-based login that sets a session cookie after verifying credentials. This moves authentication out of the browser’s native prompt and allows you to enforce frame busting and CSRF protection. Below is an example using Fastapi with password hashing and session cookies, avoiding reliance on WWW-Authenticate for sensitive actions.
from fastapi import Fastapi, Depends, HTTPException, status, Response
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.responses import HTMLResponse
import secrets
app = Fastapi()
security = HTTPBasic()
# In-memory store for demo; use a proper session store in production
sessions = {}
def verify_user(credentials: HTTPBasicCredentials):
# Replace with secure user verification, e.g., database lookup with hashed passwords
if credentials.username == "alice" and credentials.password == "secret":
token = secrets.token_urlsafe(16)
sessions[token] = credentials.username
return token
return None
@app.post("/login")
async def login(response: Response, credentials: HTTPBasicCredentials = security.credentials(request=None)):
token = verify_user(credentials)
if token is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Basic"},
)
response.set_cookie("session_token", token, httponly=True, samesite="strict", secure=True)
return {"message": "Logged in"}
@app.get("/protected")
async def protected(token: str = Cookie(None)):
if token is None or sessions.get(token) != "alice":
raise HTTPException(status_code=401, detail="Unauthorized")
return {"data": "protected"}
By using cookie-based sessions with SameSite=Strict and HttpOnly, you avoid exposing credentials to clickjacking via framed contexts. If you must support Basic Auth for compatibility, ensure that sensitive endpoints reject requests that arrive via an embedded frame by checking Referer or using a one-time token (nonce) to prevent forged requests initiated through clickjacking.