Xml External Entities in Axum with Api Keys
Xml External Entities in Axum with Api Keys — how this specific combination creates or exposes the vulnerability
Xml External Entity (XXE) injection in an Axum API becomes significantly riskier when the service relies on API keys for access control. In this combination, an attacker who does not yet have a valid key can still probe the endpoint to determine whether user-supplied XML data is processed in a way that resolves external entities. If the application parses XML with features that resolve external references, an attacker can leverage this to read local files or cause remote requests even before key validation occurs.
Consider an Axum endpoint that accepts XML payloads for configuration or data import. If the handler deserializes XML using a parser that resolves DOCTYPE and entity declarations, an attacker can submit a malicious XML body that references a file URI such as file:///etc/passwd. When the server processes the request, the parser may fetch and include that file content in the response or in server-side logs. Because the endpoint may still return detailed errors before API key verification, the attacker learns that XXE is possible, effectively bypassing an intended access boundary enforced by API keys.
In a real-world scenario, an attacker might send an XML request with an external entity declaration and an entity reference that points to a sensitive file. If the Axum application uses a Rust XML parser configured to expand entities, the parser resolves the reference and returns the data. This can expose database connection strings, service account credentials, or environment variables that are stored on the filesystem. Even when API keys are required, improper routing or middleware ordering might allow the request to reach the XML parsing stage before the key is validated, effectively weakening the intended protection.
Exposure is further amplified when the API returns verbose error messages that include stack traces or partial file contents. These responses can reveal paths, internal hostnames, or application structure, aiding further attacks. Because API keys are often passed in headers, an attacker can separately test whether keys are accepted while experimenting with XXE payloads to confirm the parser behavior. This combination means that authentication via API keys does not automatically prevent information leakage through XML processing if the underlying parser is not hardened.
For compliance mappings, findings from such a scan can align with OWASP API Top 10 A03:2023 — Injection, and relevant standards such as PCI-DSS and SOC2 that require controls against unauthorized data access. middleBrick tests this combination by submitting crafted XML with external entity references and analyzing whether the response reflects external data or error details, providing a finding with severity and remediation guidance rather than attempting to fix or block the request.
Api Keys-Specific Remediation in Axum — concrete code fixes
To reduce risk, ensure that XML parsing in Axum does not resolve external entities and that API key validation occurs before any deserialization. Use a secure XML reader that explicitly disables external entity resolution and DTD processing. Below are concrete code examples for Axum that demonstrate a safe approach.
1. Reject XML entirely where possible
If your API does not need XML, avoid parsing it. Prefer JSON and validate strict schemas. This eliminates XXE by design.
2. Use a safe XML parser configuration
When XML must be accepted, configure the parser to prohibit external entities. The following example uses the roxmltree crate, which does not expand entities by design, and validates input against a strict schema before processing.
use axum::{{
routing::post,
Router,
response::IntoResponse,
Json,
http::StatusCode,
}};
use roxmltree::Document;
use serde::Deserialize;
#[derive(Deserialize)]
struct ApiKeyHeader {
#[serde(rename = "X-API-Key)]
api_key: String,
}
async fn handle_xml_with_key(
headers: axum::extract::HeaderMap,
body: String,
) -> Result {
// Step 1: Validate API key before parsing any content
let api_key = headers.get("X-API-Key")
.ok_or((StatusCode::UNAUTHORIZED, "Missing API key".to_string()))?;
if api_key != "expected-secure-key" {
return Err((StatusCode::FORBIDDEN, "Invalid API key".to_string()));
}
// Step 2: Parse XML safely without external entity expansion
let doc = Document::parse(&body)
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid XML: {}", e)))?;
// Step 3: Process data from the document safely
let root_value = doc.root_element();
// Example: extract a specific field
let data = root_value
.children()
.find(|n| n.has_tag_name("data"))
.and_then(|n| n.text())
.unwrap_or("");
Ok((StatusCode::OK, format!("Received: {}", data)))
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/import", post(handle_xml_with_key));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
3. Middleware ordering and error handling
Place API key validation as an early layer. Avoid returning stack traces or file contents in error responses. Use generic error messages and log detailed issues server-side without exposing them to the client.
4. Alternative safe crates
If you require more complex XML features, choose crates that explicitly opt out of DTD and external entity resolution. Do not enable features that allow network access or local file reads. Test the configuration by submitting a malicious XML with an external entity and confirming that the parser rejects or ignores it.
5. Continuous monitoring
With the Pro plan, you can enable continuous monitoring so that any regression in API security configuration triggers alerts. The GitHub Action can fail builds if a scan detects high-severity findings, helping maintain secure defaults across deployments.