Xml Bomb in Axum (Rust)
Xml Bomb in Axum with Rust — how this specific combination creates or exposes the vulnerability
An XML Bomb (also known as the Billion Laughs attack) leverages exponential entity expansion in XML parsers to consume large amounts of memory and CPU. When Axum routes receive XML payloads and pass them to a Rust XML parser without constraints, the server can be forced to allocate excessive resources, causing denial of service. This specific combination — Axum’s async request handling and a permissive Rust XML parser configuration — exposes the attack surface if the parser is not explicitly hardened.
In Rust, common XML crates such as xml-rs or quick-xml allow entity definitions by default unless the parser is configured to disable them. An attacker can send a small payload that expands to gigabytes of data, exhausting memory or tying up async execution resources. Because Axum applications often parse XML for configuration or legacy integration endpoints, failing to secure the parser means the framework itself does not mitigate the bomb. The risk is not in Axum’s routing logic, but in how the application-level XML parser is initialized and used within handlers.
For example, if a handler deserializes XML using a reader that permits DOCTYPE entity expansion, a payload like the following can trigger exponential growth:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE lolz [ <!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
>
<data>&lol4;</data>
Without parser limits, this benign-looking XML can cause severe resource consumption. middleBrick’s Input Validation checks flag such patterns during scans, highlighting the need to treat XML input as untrusted. Because Axum does not provide built-in XML hardening, developers must explicitly configure parsers to reject or limit entity expansion and external references.
Rust-Specific Remediation in Axum — concrete code fixes
To mitigate XML Bombs in Axum, configure your XML parser to disable DOCTYPE processing and external entity resolution. In Rust, this is a parser-level setting rather than a framework-level concern. Below are concrete, idiomatic Axum handler examples using quick-xml and xml-rs with secure defaults.
Using quick-xml with disabled entity expansion
quick-xml provides event-driven parsing and allows disabling DTD and entity processing. Use Reader::config to turn off external entity resolution and DTD handlers.
use axum::{routing::post, Router};
use quick_xml::events::Event;
use quick_xml::reader::Reader;
use std::io::Cursor;
async fn parse_xml_handler(body: String) -> (String, usize) {
let mut reader = Reader::from_str(&body);
// Disable DTD and entity expansion to prevent XML Bomb
reader.config_mut().trim_text(true);
// Ensure no DTD events are processed
let mut buf = Vec::new();
let mut total_len = 0;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Eof) => break,
Ok(_) => {
// Safe processing of non-DTD events
total_len += 1;
}
Err(e) => {
// Handle parse error appropriately
return (format!("Error: {:?}", e), total_len);
}
}
buf.clear();
}
("success".to_string(), total_len)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/parse", post(parse_xml_handler));
// axum::Server::bind(...).serve(app.into_make_service()).await.unwrap();
}
Using xml-rs with external general entities disabled
The xml-rs crate can be configured to disallow external entities by avoiding EntityReader and using a standard EventReader with a custom parser that rejects DOCTYPE. While xml-rs does not expose a direct flag, you can enforce safety by not providing a custom entity resolver and by limiting input size before parsing.
use axum::{routing::post, Router};
use std::io::Cursor;
use xml::reader::{EventReader, XmlEvent};
async fn safe_xml_parser(body: String) -> Result<(), String> {
// Limit input size before parsing to mitigate resource exhaustion
if body.len() > 1_000_000 {
return Err("Payload too large".to_string());
}
let cursor = Cursor::new(body);
let parser = EventReader::new(cursor);
for event in parser {
match event {
Ok(XmlEvent::StartElement { name, .. })
| Ok(XmlEvent::EndElement { name })
| Ok(XmlEvent::Characters(_))
| Ok(XmlEvent::ProcessingInstruction { .. })
| Ok(XmlEvent::CData(_)) => {
// Safe handling
}
Ok(XmlEvent::StartDocument { .. })
| Ok(XmlEvent::EndDocument)
| Ok(XmlEvent::Whitespace(_))
| Ok(XmlEvent::Comment(_)) => {
// Allowed but not required to process
}
Err(e) => return Err(format!("XML error: {:?}", e)),
}
}
Ok(())
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/safe-xml", post(|body: String| async move {
safe_xml_parser(body).map_err(|e| axum::http::StatusCode::BAD_REQUEST)?;
Ok(())
}));
// axum::Server::bind(...).serve(app.into_make_service()).await.unwrap();
}
Additionally, enforce input size limits and avoid feeding raw user input to legacy XML endpoints. middleBrick’s LLM/AI Security and Input Validation checks can detect risky XML patterns in your API surface, and the Pro plan’s continuous monitoring can alert you to suspicious payloads before they impact production.