Clickjacking in Rocket (Rust)
Clickjacking in Rocket with Rust — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side injection vulnerability where an attacker tricks a user into clicking on a transparent or opaque element within a page rendered inside an invisible frame. When using the Rocket web framework with Rust, the server generates HTML responses that are rendered by the browser. If those responses do not explicitly instruct browsers to prevent framing, an attacker can embed the application’s routes inside an <iframe> and overlay interactive controls, leading to unauthorized actions performed on behalf of an authenticated user.
Rocket does not automatically add anti-framing protections; this responsibility falls to the developer. A typical vulnerable route in Rocket might render a form for changing an email address or confirming a payment. If this HTML is served without Content-Security-Policy (CSP) frame-ancestors directives or X-Frame-Options headers, an attacker’s page can load the route in an embedded context. Because Rocket applications are compiled to native binaries via Rust, the runtime does not inherently sanitize or restrict framing. The combination of Rocket’s ergonomic templating and Rust’s performance can inadvertently encourage developers to overlook header configuration, assuming the framework provides browser-level protections it does not.
Consider a scenario where a user is authenticated to a Rocket application with a route /settings that accepts POST requests to update sensitive profile data. An attacker crafts a malicious page containing an invisible iframe pointing to https://api.example.com/settings and overlays a styled button labeled "Update Profile" at a different location. When the user visits the attacker’s page, their browser loads the Rocket route inside the iframe and executes the request if the user is logged in. This is a classic example of a stored or reflected UI redressing issue, not a server-side logic flaw. The vulnerability exists because the server’s responses lack frame-ancestors policies, and the Rust code does not enforce safe headers by default.
Because Rocket runs on a variety of platforms and the Rust compiler does not enforce security headers, developers must explicitly configure defenses. Scanning such endpoints with middleBrick can detect missing anti-framing controls by analyzing response headers and CSP directives, providing severity-ranked findings and remediation guidance specific to the observed Rocket routes.
Rust-Specific Remediation in Rocket — concrete code fixes
Securing Rocket endpoints against clickjacking in Rust requires explicit header injection at the route or managed state level. The most effective controls are Content-Security-Policy with frame-ancestors set to 'none' or a restricted list, and X-Frame-Options set to DENY or SAMEORIGIN. Below are concrete, compilable Rocket examples.
1. Global response guard (recommended)
Attach a managed state that adds security headers to every outgoing response. This ensures all routes benefit from framing protections without repeating code.
use rocket::{http::Header, response::Response};
use rocket::fairing::{Fairing, Info, Kind};
use rocket::request::Request;
pub struct SecurityHeaders;
#[rocket::async_trait]
impl Fairing for SecurityHeaders {
fn info(&self) -> Info {
Info {
name: "Add Security Headers",
kind: Kind::Response,
}
}
async fn on_response(&self, _request: &Request, response: &mut Response) {
// Prevent framing entirely
response.set_header(Header::new("X-Frame-Options", "DENY"));
// Modern framing policy: do not allow any frame ancestors
response.set_header(Header::new(
"Content-Security-Policy",
"frame-ancestors 'none'",
));
}
}
#[rocket::main]
async fn main() {
rocket::build()
.attach(SecurityHeaders)
.mount("/", routes![settings_post])
.launch()
.await;
}
2. Per-route header attachment
If you prefer to apply headers only to specific endpoints, attach headers directly in the responder or use a catcher.
use rocket::http::Header;
use rocket::response::content::Html;
use rocket::Request;
#[get("/settings")]
fn settings() -> Html<&'static str> {
Html("<form method=\"post\"></form>")
}
#[post("/settings")]
fn settings_post() -> (&'static str, Vec<(&'static str, String)>) {
// Return headers alongside the body when composing responses
let headers = vec![
("X-Frame-Options", "DENY".to_string()),
("Content-Security-Policy", "frame-ancestors 'none'".to_string()),
];
("Settings updated", headers)
}
3. Using Rocket’s ResponseResponder to wrap responses
For typed APIs, implement a responder that injects headers programmatically.
use rocket::{http::Status, request::Request, response::{Responder, Response, Build};
use std::io::Cursor;
struct SecureResponse('static str);
#[rocket::async_trait]
impl Responder<_, <_ as rocket::State<()>> for SecureResponse {
fn respond_to(self, req: &Request<'_>) -> rocket::response::Result {
let mut response = Response::build_from((Status::Ok, self.0)).ok_or(Status::InternalServerError)?;
response.set_header(rocket::http::Header::new("X-Frame-Options", "DENY"));
response.set_header(rocket::http::Header::new(
"Content-Security-Policy",
"frame-ancestors 'none'",
));
Ok(response)
}
}
#[get("/profile")]
fn profile() -> SecureResponse {
SecureResponse("{\"status\": \"ok\"}")
}
These Rust-specific patterns ensure that clickjacking protections are enforced consistently across a Rocket application. middleBrick can validate that the headers are present and correctly configured, reducing the attack surface for UI redressing.