Xss Cross Site Scripting in Axum with Api Keys
Xss Cross Site Scripting in Axum with Api Keys — how this specific combination creates or exposes the
Cross-site scripting (XSS) in an Axum API that uses API keys can occur when user-controlled data is reflected into HTML or JavaScript responses without proper encoding, and the API key is inadvertently exposed in those responses. XSS typically arises from insufficient input validation and output encoding, while API keys are often handled as bearer tokens in authorization headers. The combination becomes risky when debugging or error responses include the key or when a key is embedded in a URL query parameter and later reflected into the page without sanitization.
For example, if an Axum handler reads an api_key query parameter and includes its value directly in an HTML response, an attacker can supply a key containing a script payload. If the application does not encode the reflected value, the browser executes the script in the context of the site. Another scenario involves an endpoint that returns JSON containing the key and is consumed by a frontend that inserts the key into the DOM using innerHTML, enabling reflected XSS. Stored XSS is less common for pure APIs but can occur if keys are logged or echoed in admin interfaces or error pages that render HTML.
Insecure direct object references (IDOR) and broken function-level authorization (BFLA) can also intersect with XSS when an API key is used to access user-specific resources and the response includes unescaped data from those resources. For instance, an endpoint like /users/{id}/export that accepts an api_key and returns CSV or HTML without escaping user-supplied fields can lead to script execution if an attacker manipulates the exported content.
Api Keys-Specific Remediation in Axum — concrete code fixes
To remediate XSS risks in Axum APIs that use API keys, ensure keys are never reflected in responses, treat all external input as untrusted, and apply context-aware output encoding. The following practices and code examples demonstrate secure handling.
1. Avoid reflecting API keys in responses
Do not include raw API keys in JSON or HTML responses. If you must return a masked representation for debugging, use a non-reversible transformation and avoid embedding keys in executable contexts.
// Unsafe: echoing the key into JSON
async fn unsafe_handler(key: String) -> impl IntoResponse {
let body = json!({ "api_key": key });
(StatusCode::OK, body)
}
// Safer: do not return the key; return a masked token if needed
async fn safe_handler(key: String) -> impl IntoResponse {
let masked = if key.len() > 6 {
format!("****-****-****-{}", &key[key.len()-4..])
} else {
"****".to_string()
};
let body = json!({ "key_summary": masked });
(StatusCode::OK, body)
}
2. Encode data in HTML and JavaScript contexts
If responses are consumed by a web frontend, encode data based on the context. For HTML body content, use an HTML encoder; for JavaScript embedded in HTML, use a JS encoder; for URLs, use URL encoding. Axum does not provide templating by default, so encoding is typically handled by the frontend framework. However, if you generate HTML on the server, ensure proper escaping.
// Example using askama for HTML templates (askama::escape autoescapes)
#[derive(Template)]
#[template(path = "key_status.html")]
struct KeyStatus {
key_summary: String,
}
// In the handler
async fn html_handler(key: String) -> Result<impl IntoResponse, Error> {
let summary = if key.len() > 6 {
format!("****-****-****-{}", &key[key.len()-4..])
} else {
"****".to_string()
};
let template = KeyStatus { key_summary: summary };
Ok(Json(serde_json::json!({ "status": "ok" })))
}
3. Validate and sanitize inputs, enforce strict content types
Validate the format of API keys (e.g., expected length, character set) and reject suspicious patterns that could contain encoded scripts. Use strict content-type checks and avoid serving HTML from endpoints that expect JSON-only clients.
// Validate key format to mitigate injection via malformed keys
fn is_valid_key_format(key: &str) -> bool {
// Allow only alphanumeric and dashes, typical for bearer tokens
key.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
}
async fn validated_handler(key: String) -> impl IntoResponse {
if !is_valid_key_format(&key) {
return (StatusCode::BAD_REQUEST, Json(json!({ "error": "invalid_key_format" })));
}
// Proceed with business logic
(StatusCode::OK, Json(json!({ "validated": true })))
}
4. Secure logging and error handling
Ensure logs and error messages do not include full API keys. Use sanitization before logging to prevent accidental leakage that could be reflected later via an insecure interface.
// Sanitize logs
fn sanitize_log_key(key: &str) -> String {
if key.len() > 4 {
format!("****-****-****-****-****-****-****-{}", &key[key.len()-4..])
} else {
"****".to_string()
}
}
async fn logging_handler(key: String) -> impl IntoResponse {
info!(key = sanitize_log_key(&key), "key processed");
(StatusCode::NO_CONTENT, body())
}
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |