Cache Poisoning in Rocket with Bearer Tokens
Cache Poisoning in Rocket with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Cache poisoning in the Rocket web framework occurs when an attacker causes a cached response to be stored or served with content that is specific to one client, such as sensitive data associated with a Bearer token. This typically happens when caching logic does not adequately take into account request headers that carry authorization information. In Rocket, routes that accept Authorization: Bearer <token> headers may inadvertently participate in cache keys that ignore the header’s value, leading to situations where a cached response for one token is reused for another.
Consider a Rocket handler that reads a Bearer token from the request header but does not include the token in the cache key. If the route is marked as cacheable by an intermediate proxy or by Rocket’s own caching mechanisms, the first request with a valid token may be cached. Subsequent requests with different Bearer tokens may receive the cached response, exposing the data of the first client to others. This is especially risky when responses contain user-specific or role-specific data, such as account details or scoped permissions, because the cache disregards the security boundary enforced by the token.
The vulnerability is compounded when the Bearer token is passed in headers that are not considered part of the cache key by default. Rocket’s request guards and fairings can inspect and validate tokens, but if caching is applied at the routing or proxy layer without accounting for the Authorization header, the security boundary is bypassed. Attackers may leverage this to view data of other users or escalate privileges by observing cached responses that should have been isolated per token. Real-world attack patterns such as confused deputy or token leakage through shared caches are relevant here, and findings may map to OWASP API Top 10 A01: Broken Object Level Authorization and A05: Security Misconfiguration.
When scanning such endpoints with middleBrick, checks for Data Exposure and Authentication are run in parallel, identifying cases where Authorization headers are not factored into cache behavior. The scanner also evaluates Input Validation and Rate Limiting to ensure that token handling does not introduce additional weaknesses. Because Rocket applications often integrate with external caching layers or CDNs, it is important to ensure that any caching mechanism includes the full request context that affects authorization, including the Bearer token value or its scope, to prevent inadvertent data mixing across clients.
Bearer Tokens-Specific Remediation in Rocket — concrete code fixes
To remediate cache poisoning risks when using Bearer tokens in Rocket, ensure that any caching mechanism incorporates the Authorization header or a derived, non-sensitive scope value into the cache key. Avoid caching responses that contain user-specific data unless the cache key explicitly includes a per-user or per-token component. Below are concrete patterns to achieve this in Rocket.
Example 1: Skipping cache for authenticated requests
Use a request guard to bypass caching for routes that require authentication. This ensures that responses for bearer-authenticated requests are not stored in shared caches.
use rocket::request::Request;
use rocket::fairing::AdHoc;
use rocket::http::Status;
fn ensure_no_cache(request: &mut Request) {
if let Some(auth_header) = request.headers().get_one("Authorization") {
if auth_header.starts_with("Bearer ") {
// Prevent caching by setting headers that discourage shared caches
request.set_header(rocket::http::Header::new("Cache-Control", "no-store, no-cache"));
request.set_header(rocket::http::Header::new("Pragma", "no-cache"));
}
}
}
#[rocket::main]
async fn main() {
rocket::build()
.attach(AdHoc::on_request("No-Cache for Bearer", |req| Box::pin(async move {
ensure_no_cache(req);
})))
.mount("/", routes![secure_data])
.launch()
.await;
}
#[rocket::get("/account")]
fn secure_data() -> String {
"User-specific data".to_string()
}
Example 2: Incorporating token scope into cache key
If caching is required, derive a cache key that includes the scope or a hash of the token’s claims rather than the raw token. This allows safe caching for public, non-sensitive data while isolating user-specific responses.
use rocket::request::Request;
use rocket::http::Status;
use rocket::response::content::Json;
use serde_json::json;
fn extract_scope_from_bearer(req: &Request) -> Option {
req.headers().get_one("Authorization")
.and_then(|h| h.strip_prefix("Bearer "))
.map(|token| {
// In practice, decode the JWT or map token to scope via a lookup
// Here we simulate scope derivation
if token == "token_admin" { "admin".to_string() } else { "user".to_string() }
})
}
#[rocket::get("/data")]
fn cached_data(req: &Request) -> Json<serde_json::Value> {
let scope = extract_scope_from_bearer(req).unwrap_or("default".to_string());
// Use scope as part of the cache key in a real implementation
Json(json!({
"scope": scope,
"data": "public-ish data"
}))
}
Example 3: Using fairings to control cache behavior
A fairing can inspect the Authorization header and modify caching headers or route selection to ensure token-aware handling.
use rocket::fairing::{Fairing, Info, Kind};
use rocket::request::Request;
use rocket::http::Header;
struct TokenAwareCache;
impl Fairing for TokenAwareCache {
fn info(&self) -> Info {
Info {
name: "Token-Aware Cache Control",
kind: Kind::Response,
}
}
fn on_response(&self, request: &Request, response: &mut rocket::response::Response) {
if let Some(auth) = request.headers().get_one("Authorization") {
if auth.starts_with("Bearer ") {
response.set_header(Header::new("Cache-Control", "private, no-store"));
}
}
}
}
#[rocket::main]
async fn main() {
rocket::build()
.attach(TokenAwareCache)
.mount("/", routes![secure_data])
.launch()
.await;
}
These examples demonstrate how to align caching behavior with the security boundaries defined by Bearer tokens. By including token scope or avoiding caching for authenticated requests, you reduce the risk of cache poisoning. Findings from middleBrick’s scans can guide which routes require these controls and help verify that headers like Authorization are properly considered in any caching layer.