Cache Poisoning in Rocket with Mutual Tls
Cache Poisoning in Rocket with Mutual Tls — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker tricks a server or intermediary into storing a malicious response for subsequent users. In Rocket, this can happen when responses are cached based on insufficient request validation, and mutual TLS (mTLS) is used only for client authentication at the edge or gateway without being considered by the application-layer cache logic.
When mTLS is enforced, the server presents a certificate and requests a client certificate. Rocket validates the client certificate and may extract principal information (e.g., subject or SAN) to identify the peer. However, if the application uses request attributes that are not part of the TLS identity to construct cache keys—such as headers, query parameters, or cookies—an attacker who can present a valid client certificate might supply crafted inputs that cause a poisoned entry to be stored under a key that appears legitimate to other clients.
For example, suppose Rocket uses the X-User-ID header to personalize responses and caches responses keyed on that header. Even with mTLS ensuring the request comes from a trusted client, an attacker with a valid certificate can send a request with X-User-ID: attacker_id. If the response is cached based on this header, subsequent legitimate users might receive the attacker’s cached content. This violates separation between identities and can expose private data or alter behavior for downstream users.
Additionally, if Rocket’s routing or caching logic inadvertently relies on mutable or untrusted request properties—such as Host, Referer, or non-authoritative parameters—while mTLS only authenticates the transport layer, the cache may incorrectly treat distinct requests as equivalent. This is a Cache Poisoning vector specific to the combination of mTLS authenticated endpoints and application-managed caching that does not incorporate the mTLS identity into the cache key or validation rules.
In this context, mTLS does not prevent Cache Poisoning; it only ensures that the peer presenting a request is authenticated. If the application does not factor the authenticated identity into cache determinism, an authorized client can still poison the cache for other authorized clients. This highlights the need to align caching strategy with the security boundary established by mTLS.
Mutual Tls-Specific Remediation in Rocket — concrete code fixes
To mitigate Cache Poisoning when using mutual TLS in Rocket, ensure that cache keys incorporate the mTLS identity and that responses are segregated by authenticated principal. Avoid using attacker-controllable request attributes as sole cache keys when mTLS is used for authentication.
Below are concrete code examples for configuring Rocket with mTLS and building cache-aware request handling.
1. Configure Rocket with mTLS (Rocket 0.5+)
Set up TLS for the server and require client certificates. Use tls configuration to specify paths to certificates and enforce client verification.
use rocket::Build;
use rocket::figment::providers::{Format, Toml};
#[rocket::main]
async fn main() {
let figment = rocket::Config::figment()
.merge((
"tls.certs_path", "/path/to/certs",
"tls.key_path", "/path/to/certs/server.key",
"tls.cert_path", "/path/to/certs/server.crt",
"tls.client_ca_path", "/path/to/certs/ca.crt",
"tls.client_auth", "required",
));
rocket::custom(figment).mount("/", routes![index]).launch().await.unwrap();
}
2. Extract mTLS identity and use it in cache key
In a request guard, extract the client’s certificate subject or SAN and combine it with business keys to form a cache-safe identifier. This prevents cross-client cache poisoning.
use rocket::request::{self, FromRequest, Request};
use rocket::http::Status;
use std::sync::Arc;
struct AuthenticatedClient {
subject: String,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthenticatedClient {
type Error = ();
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
// In practice, extract the peer certificate from the TLS session.
// This example uses a placeholder; integration with your TLS stack will vary.
match req.tls_client_cert() {
Some(cert) => {
let subject = cert.subject().to_string(); // e.g., "/CN=alice"
request::Outcome::Success(AuthenticatedClient { subject })
}
None => request::Outcome::Failure((Status::Unauthorized, ())),
}
}
}
// Example route that uses the authenticated client identity in cache logic.
#[get("/data")]
fn get_data(client: AuthenticatedClient, query: String) -> String {
let cache_key = format!("{}:{}", client.subject, query);
// Use cache_key with your caching backend to store/retrieve responses.
format!("Response for {}", cache_key)
}
3. Avoid caching on attacker-controllable inputs
Do not allow headers or parameters that an authenticated client can manipulate to directly define cache entries unless they are combined with the mTLS identity. If you must use such inputs, treat them as part of a composite key that includes the authenticated principal.
#[get("/profile?user_id<user_id>")]
fn profile(user_id: u64, client: AuthenticatedClient) -> String {
// Safe: user_id combined with client subject ensures cache segregation.
let composite_key = format!("user:{}:client:{}", user_id, client.subject);
// Proceed with lookup/store using composite_key
format!("Profile {}", composite_key)
}
4. Middleware to normalize cache behavior
Implement a fairing or request guard that validates and normalizes inputs before they reach route handlers. This centralizes the logic that prevents cache poisoning and keeps mTLS identity at the core of cache determinism.
use rocket::fairing::{Fairing, Info, Kind};
use rocket::request::{self, Request};
struct CacheKeyFairing;
impl Fairing for CacheKeyFairing {
fn info(&self) -> Info {
Info {
name: "Cache Key Fairing",
kind: Kind::Request,
}
}
fn on_request(&self, req: &mut Request<_>) { /* no-op; handled in guards */ }
}
// Use the guard pattern shown above to enforce mTLS identity in cache keys.
By coupling mTLS authentication with explicit, identity-aware cache keys, you reduce the risk of Cache Poisoning in Rocket applications. Remember that middleBrick scans can help detect misconfigurations related to exposed or inconsistent caching behavior, but it does not fix or block issues; it provides findings and remediation guidance.