Clickjacking in Actix with Bearer Tokens
Clickjacking in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side attack where an attacker tricks a user into interacting with a hidden or disguised UI element, often by loading the target site inside an invisible iframe. When a web service relies on Bearer Tokens for authentication and does not enforce anti-clickjacking protections, the combination can expose privileged actions to exploitation. In Actix, a common pattern is to validate the presence of a Bearer Token in the Authorization header for API endpoints while serving HTML or API responses that may be embedded elsewhere.
Consider an Actix web application that exposes an account deletion endpoint protected by a Bearer Token. If the response headers do not include X-Frame-Options or Content-Security-Policy (CSP) with a frame-ancestors directive, an attacker can craft a page that loads this endpoint inside an iframe. Even though the browser will send the Bearer Token automatically with the request (assuming the token is stored in an Authorization header and not vulnerable to cross-origin leakage), the UI interaction (e.g., a forged form submission or a crafted JavaScript event) can be designed to trigger the action without the user’s knowledge.
The risk is elevated when the Bearer Token is stored in a way that is accessible to embedded contexts (for example, in JavaScript-accessible storage), and the Actix application does not validate the Origin or Referer headers strictly. While Bearer Tokens protect the request from being made by unauthorized parties without the token, they do not prevent the browser from including the token in requests initiated by malicious pages if the endpoint lacks frame restrictions. This means an attacker can leverage a victim’s authenticated session to perform actions such as changing email addresses, updating payment methods, or disabling security settings, all while the user believes they are interacting with a benign page.
In an Actix-based API that also serves UI or hybrid server-rendered pages, missing CSP headers or misconfigured CORS settings can further enable clickjacking by allowing the application’s own routes to be framed. For instance, an endpoint that returns a form for sensitive operations might be framed by an attacker’s site, and without proper frame-busting techniques or CSP, the user’s browser will render it as part of the malicious page. The presence of a Bearer Token does not mitigate this, as the token is simply sent by the browser in accordance with its normal credential storage policies.
To understand the exposure, consider scanning such an endpoint with middleBrick. The scanner’s checks include property authorization and input validation, and it can detect missing anti-framing headers across responses, even when Bearer Token authentication is present. middleBrick’s reports will highlight missing CSP frame-ancestors or absent X-Frame-Options, helping you correlate authentication mechanisms with framing risks.
Bearer Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on preventing the page from being embedded in frames and ensuring that Bearer Token usage does not inadvertently enable clickjacking workflows. In Actix, you can enforce framing protections at the application or middleware level. Below are concrete patterns and code examples.
1. Set CSP and X-Frame-Options headers
For any response that could be rendered in a browser context (including API responses that might be accidentally framed), add security headers. Use Content-Security-Policy with frame-ancestors set to 'none' or a strict allowlist, and include X-Frame-Options for legacy browser compatibility.
use actix_web::{web, App, HttpResponse, HttpServer, Responder, middleware::Logger};
use actix_web::http::header::{self, HeaderValue};
async fn delete_account() -> impl Responder {
let mut resp = HttpResponse::Ok().body("Account deletion processed");
resp.headers_mut().insert(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src 'self'; frame-ancestors 'none'"),
);
resp.headers_mut().insert(
header::X_FRAME_OPTIONS,
HeaderValue::from_static("DENY"),
);
resp
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.route("/api/account/delete", web::post().to(delete_account))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
2. Use middleware to enforce frame protection globally
Instead of adding headers to every handler, define a middleware that injects security headers on all responses. This ensures that even if a developer forgets to add them, framing is still restricted.
use actix_web::{dev::{Service, ServiceResponse, Transform}, Error, HttpMessage, HttpResponse};
use actix_web::http::header::{self, HeaderValue};
use futures_util::future::{ok, Ready};
use std::task::{Context, Poll};
pub struct SecurityHeaders;
impl Transform for SecurityHeaders
where
S: Service, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type Transform = SecurityHeadersMiddleware;
type InitError = ();
type Future = Ready>;
fn new_transform(&self, service: S) -> Self::Future {
ok(SecurityHeadersMiddleware { service })
}
}
pub struct SecurityHeadersMiddleware {
service: S,
}
impl Service for SecurityHeadersMiddleware
where
S: Service, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type Future = std::pin::Pin>>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
let fut = self.service.call(req);
Box::pin(async move {
let mut res = fut.await?;
res.headers_mut().insert(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src 'self'; frame-ancestors 'none'"),
);
res.headers_mut().insert(
header::X_FRAME_OPTIONS,
HeaderValue::from_static("DENY"),
);
Ok(res)
})
}
}
// In your App builder:
// .wrap(SecurityHeaders)
3. Avoid exposing Bearer Tokens in browser-accessible contexts
If your Actix application serves pages that include API calls requiring Bearer Tokens, ensure tokens are not embedded in JavaScript or stored in locations accessible to client-side scripts that could be framed. Prefer short-lived tokens and use strict CORS to limit origins that can invoke sensitive endpoints. Note that Bearer Tokens in headers are not sufficient alone; you must also ensure the application does not inadvertently allow framing of authenticated pages.
4. Validate Origin and Referer for state-changing operations
While not a replacement for CSP, validating the Origin or Referer header on sensitive POST requests can add a layer of defense. This is particularly useful when combined with the header-based protections above.
async fn sensitive_operation(req: HttpRequest) -> Result {
let origin = req.headers().get(header::ORIGIN);
let referer = req.headers().get(header::REFERER);
// Implement strict origin/referer checks against an allowlist
// For example, ensure origin matches your known frontend origins
if let Some(o) = origin {
if o != "https://trusted.example.com" {
return Err(error::ErrorForbidden("Invalid origin"));
}
}
// Proceed with operation
Ok(HttpResponse::Ok().body("OK"))
}