Api Key Exposure in Rocket
How Api Key Exposure Manifests in Rocket
Api Key Exposure in Rocket applications typically occurs through improper handling of authentication credentials in API endpoints. Rocket's flexible request handling can inadvertently expose API keys through several vectors that developers might overlook.
The most common manifestation involves API keys being logged in plaintext. When Rocket processes requests with API keys in headers or query parameters, developers might log entire request objects or use debug logging that captures sensitive headers. For example, a simple debug statement like println!("Request: {:?}", req) can expose API keys in production logs.
Query parameter exposure represents another significant risk. Rocket's query parameter extraction automatically parses URL parameters, and developers might accidentally expose API keys through error messages or logging. Consider this vulnerable endpoint:
#[get("/api/data?&&")]
fn get_data(api_key: String, param1: String, param2: String) -> Json<Response> {
// API key now exists as a plain String in memory
log::debug!("Processing request with api_key: {}", api_key);
// ...
} The API key is now stored as a plain String, logged in debug output, and potentially exposed through error messages if the function panics.
Middleware and request guards can also create exposure points. Rocket's FromRequest trait allows custom authentication, but improper implementation can lead to API key leakage. A naive implementation might look like:
struct ApiKey(String);
impl<'a, 'r> FromRequest<'a, 'r> for ApiKey {
type Error = &'static str;
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
match request.headers().get_one("x-api-key") {
Some(key) if key.len() > 0 => Outcome::Success(ApiKey(key.to_string())),
_ => Outcome::Failure((Status::Unauthorized, "Missing API key"))
}
}
}
#[get("/api/protected")]
fn protected_endpoint(key: ApiKey) -> Json<Response> {
// API key accessible as key.0
// If this endpoint errors, the API key might be exposed in error responses
}The vulnerability here is that the API key is stored as a plain String and passed directly to the handler, making it accessible to any code that might log or expose it.
Environment variable mishandling also contributes to API key exposure. Developers might accidentally commit Rocket configuration files containing API keys, or use debug builds that print configuration values. Rocket's configuration system allows loading from various sources, and improper handling of these sources can lead to exposure.
Rocket-Specific Detection
Detecting API key exposure in Rocket applications requires a multi-faceted approach combining static analysis, runtime monitoring, and automated scanning.
Static analysis tools can identify vulnerable patterns in Rocket codebases. Look for these specific patterns:
# Search for API key handling patterns
rg -n "api_key|API_KEY|auth|Auth" --type rust
# Find query parameter extraction with sensitive names
rg -n "get_one.*api_key|get_one.*auth" --type rust
# Locate logging of request objects
rg -n "println.*request|debug.*request|log.*request" --type rust
Manual code review should focus on request guards and parameter extraction. Examine all FromRequest implementations and query parameter handlers for proper API key handling. Pay special attention to error handling paths where API keys might be inadvertently exposed.
Runtime monitoring can detect API key exposure through log analysis. Configure Rocket's logging to capture all request processing, then analyze logs for API key patterns:
# Monitor logs for API key patterns
journalctl -u rocket-app -f | grep -E "(api_key|API_KEY|auth_token|Auth-Token)"
# Check for sensitive data in error responses
curl -s http://localhost:8000/api/endpoint | grep -E "(api_key|API_KEY|auth_token)"
Automated scanning with middleBrick provides comprehensive API key exposure detection. middleBrick's black-box scanning approach tests your Rocket API endpoints without requiring source code access or credentials. The scanner examines:
- Request headers for API key patterns
- Query parameters containing authentication tokens
- Response bodies for leaked credentials
- Logging endpoints and debug interfaces
- Configuration endpoints that might expose API keys
middleBrick's LLM/AI Security module specifically detects system prompt leakage patterns that might contain API keys, using 27 regex patterns to identify various prompt formats including ChatML, Llama 2, Mistral, and Alpaca formats.
To scan your Rocket API with middleBrick:
# Using the CLI tool
middlebrick scan https://your-rocket-api.com/api
# Using the npm package
npx middlebrick scan https://your-rocket-api.com/api
# Using the GitHub Action
- name: Scan Rocket API
uses: middlebrick/middlebrick-action@v1
with:
api_url: https://your-rocket-api.com/api
fail_below_score: 80
middleBrick's 12 security checks run in parallel and specifically test for authentication-related vulnerabilities, including API key exposure through various attack vectors. The scanner provides actionable findings with severity levels and remediation guidance tailored to your specific vulnerability.
Rocket-Specific Remediation
Remediating API key exposure in Rocket requires implementing secure handling patterns and leveraging Rocket's built-in security features. The goal is to ensure API keys are never exposed in logs, error messages, or response bodies.
The first step is implementing proper request guards that don't store API keys as plain Strings. Use Rocket's Secret type or custom secure wrappers:
use rocket::request::{FromRequest, Request};
use rocket::http::Status;
use rocket::outcome::Outcome;
struct SecureApiKey(Option<String>);
impl<'a, 'r> FromRequest<'a, 'r> for SecureApiKey {
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
let keys: Vec<_> = request.headers().get("x-api-key").collect();
if keys.len() == 1 {
// Store as Option to prevent accidental logging
Outcome::Success(SecureApiKey(Some(keys[0].to_string())))
} else {
Outcome::Failure((Status::Unauthorized, ()))
}
}
}
#[get("/api/protected")]
fn protected_endpoint(key: SecureApiKey) -> Json<Response> {
// API key is wrapped and not directly accessible
// Implement authorization logic without exposing the key
if let SecureApiKey(Some(api_key)) = key {
// Use api_key internally without logging or exposing
validate_and_process(api_key)
} else {
Json(Response::error("Unauthorized"))
}
}
fn validate_and_process(api_key: &str) -> Json<Response> {
// Never log the API key
// Implement proper validation without exposing credentials
if is_valid_api_key(api_key) {
Json(Response::success("Authorized"))
} else {
Json(Response::error("Invalid API key"))
}
}
fn is_valid_api_key(key: &str) -> bool {
// Implement secure validation without exposing the key
// Example: compare against hashed values
let stored_hash = get_stored_hash_for_key(key);
verify_hash(key, stored_hash)
}
This implementation prevents accidental API key exposure by wrapping the key in a struct that doesn't implement Debug or other traits that might cause logging.
Configure Rocket's logging to exclude sensitive information. Create a custom logger that filters out API keys:
use log::{Level, Record};
use std::sync::Mutex;
struct ApiKeyFilterLogger;
impl log::Log for ApiKeyFilterLogger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= Level::Debug
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let msg = record.args().to_string();
// Filter out API key patterns
if !msg.contains("api_key") && !msg.contains("API_KEY") {
println!("[{}] {}", record.level(), msg);
}
}
}
fn flush(&self) {}
}
pub fn init_logger() {
log::set_boxed_logger(Box::new(Mutex::new(ApiKeyFilterLogger))).unwrap();
log::set_max_level(log::LevelFilter::Info);
}
Implement proper error handling that doesn't expose API keys in error messages. Use generic error responses:
#[derive(Serialize)]
struct ApiError {
error: String,
code: u16,
}
impl ApiError {
fn unauthorized() -> Self {
ApiError {
error: "Unauthorized".to_string(),
code: 401
}
}
fn invalid() -> Self {
ApiError {
error: "Invalid request".to_string(),
code: 400
}
}
}
#[catch(401)]
fn unauthorized_catcher() -> Json<ApiError> {
Json(ApiError::unauthorized())
}
#[catch(400)]
fn bad_request_catcher() -> Json<ApiError> {
Json(ApiError::invalid())
}
fn main() {
rocket::ignite()
.register(catchers![unauthorized_catcher, bad_request_catcher])
.launch();
}
Configure Rocket to use environment variables for API keys without exposing them in configuration files. Use Rocket's configuration system securely:
use rocket::config::{Config, Environment};
fn build_config() -> Config {
let api_key = std::env::var("API_KEY").expect("API_KEY must be set");
// Never log the API key
let config = Config::build(Environment::Production)
.address("0.0.0.0")
.port(8000)
.finalize()
.unwrap();
config
}
fn main() {
let config = build_config();
rocket::custom(config)
.mount("/api", routes![protected_endpoint])
.launch();
}
Finally, integrate continuous security scanning into your development workflow using middleBrick's GitHub Action to automatically scan your Rocket API endpoints before deployment:
name: Security Scan
on: [pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Scan Rocket API
uses: middlebrick/middlebrick-action@v1
with:
api_url: ${{ secrets.ROCKET_API_URL }}
fail_below_score: 85
token: ${{ secrets.MIDDLEBRICK_TOKEN }}
- name: Report results
run: |
echo "API security scan completed"
This comprehensive approach ensures API keys are properly handled throughout your Rocket application's lifecycle, from development through production deployment.