HIGH actixxml bomb

Xml Bomb in Actix

How Xml Bomb Manifests in Actix

An XML bomb (also known as a billion laughs attack) is a denial-of-service technique that exploits XML entity expansion. The attacker crafts an XML document containing nested entity definitions that expand exponentially when parsed, causing the parser to consume excessive memory and potentially crash the application. This is cataloged as CWE-776 (Improper Restriction of Recursive Entity References in DTDs).

In Actix applications, the most common entry point for this vulnerability is the web::Xml extractor. This extractor relies on serde-xml-rs, which in turn uses the xml-rs parser. The xml-rs parser does not impose any limits on entity expansion by default. When it encounters a document with entities like the example below, it will attempt to construct the fully expanded string in memory, regardless of the original document size.

<?xml version="1.0"?>
<!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;">
]>
<root>&lol4;</root>

This payload starts under 1KB but expands to approximately 100 billion "lol" strings. Even with Actix's default payload limit of 256KB, the expansion occurs after the payload is read, so memory consumption can reach gigabytes. A single request can exhaust the process memory, leading to a crash or triggering the operating system's OOM killer, resulting in a denial of service for all users.

The vulnerability is not limited to the web::Xml extractor; any direct use of xml-rs or serde-xml-rs to parse untrusted XML is susceptible. Actix itself does not provide built-in protection against this pattern, so developers must explicitly choose a safe parser or configure limits.

Actix-Specific Detection

To identify XML bomb vulnerabilities in an Actix codebase, start by searching for the web::Xml extractor in route handlers and for xml-rs or serde-xml-rs in Cargo.toml. For example:

use actix_web::{web, HttpResponse, Responder};

async fn vulnerable_handler(data: web::Xml<MyData>) -> impl Responder {
    // Process data
    HttpResponse::Ok().body("Received")
}

If such code exists and the endpoint accepts XML (Content-Type: application/xml), it is likely vulnerable because xml-rs has no entity expansion limits.

Dynamic testing is also essential. You can manually send a small XML bomb payload to the endpoint and monitor for abnormal behavior (e.g., long response times, 500 errors, or process termination). However, a more systematic approach is to use a security scanner like middleBrick. middleBrick's Input Validation check includes testing for XML bomb vulnerabilities. It sends a carefully crafted, non-disruptive payload that expands to a few megabytes and analyzes the response for signs of excessive processing (such as timeouts or error messages). This black-box testing works without requiring access to source code or credentials.

To scan an Actix API with middleBrick, simply provide the endpoint URL:

middlebrick scan https://api.example.com/xml-endpoint

If the endpoint is vulnerable, middleBrick will generate a finding under the "Input Validation" category, assign a severity based on the observed impact, and provide remediation guidance specific to Actix. The scan takes only 5–15 seconds and requires no configuration. For APIs documented with OpenAPI, middleBrick automatically discovers all XML-consuming endpoints and tests them, even if they are not directly known.

You can also integrate middleBrick into your CI/CD pipeline using the middlebrick GitHub Action. This allows you to fail builds if a new XML bomb vulnerability is introduced or if an existing one re-appears. The CLI tool and web dashboard let you track scores over time, helping you ensure that your Actix services remain resilient against this class of attack.

Actix-Specific Remediation

The most effective fix is to replace the vulnerable xml-rs parser with quick-xml, which does not support DTDs or entity expansion at all. This is safe for most API use cases where DTDs are unnecessary. Here's how to remediate an Actix application:

Step 1: Update dependencies
Remove serde-xml-rs and xml-rs from Cargo.toml and add quick-xml with the serde feature:

[dependencies]
quick-xml = { version = "0.31", features = ["serde"] }
# Remove serde-xml-rs and xml-rs

Step 2: Replace the XML extractor
Because Actix does not provide a built-in Xml extractor for quick-xml, you need to implement a custom extractor or parse manually. The following custom extractor validates the content type and uses quick-xml to deserialize safely:

use actix_web::{web, FromRequest, HttpRequest, Error};
use quick_xml::de::from_slice;
use serde::Deserialize;

pub struct Xml<T>(pub T);

impl<T> FromRequest for Xml<T>
where
    T: for<'de> Deserialize<'de>,
{
    type Error = Error;
    fn from_request(req: &HttpRequest, payload: &mut actix_web::dev::Payload) -> Future<Self::Result> {
        // Ensure the request has an XML content type
        if !req.content_type().contains("xml") {
            return futures_util::future::ready(Err(actix_web::error::ErrorUnsupportedMediaType(
                "Expected XML content",
            )));
        }

        // Read the payload with Actix's configured limit
        let bytes = match web::Bytes::from_request(req, payload).await {
            Ok(b) => b,
            Err(e) => return futures_util::future::ready(Err(e)),
        };

        // Parse with quick-xml (no entity expansion)
        match from_slice(&bytes) {
            Ok(data) => futures_util::future::ready(Ok(Xml(data))),
            Err(e) => futures_util::future::ready(Err(actix_web::error::ErrorBadRequest(e))),
        }
    }
}

Then update your handler to use the new extractor:

async fn process_xml(data: Xml<MyData>) -> impl Responder {
    HttpResponse::Ok().body(format!("Hello, {}!", data.0.name))
}

Step 3: Enforce payload size limits
Even though quick-xml is safe from entity expansion, you should still limit the maximum request body size to prevent memory exhaustion from extremely large XML documents. Set a global limit in your Actix app:

use actix_web::{web, App, HttpServer};

HttpServer::new(|| {
    App::new()
        .app_data(web::PayloadConfig::new(1_000_000)) // 1 MB limit
        // ... routes
})

Alternatively, apply a limit per route using .app_data().

Step 4: Verify the fix
After deploying the changes, rescan the endpoint with middleBrick. The Input Validation finding should disappear, and your risk score will improve. Remember that middleBrick's scoring is based on the presence and severity of vulnerabilities, so eliminating this issue will raise your grade (e.g., from C to B).

By following these steps, you ensure that your Actix API is resilient to XML bomb attacks while maintaining compatibility with standard XML payloads that do not use DTDs.

Frequently Asked Questions

I'm not using web::Xml but I parse XML with xml-rs directly. Am I still vulnerable?
Yes. Any use of xml-rs or serde-xml-rs to parse untrusted XML is vulnerable because the parser does not restrict entity expansion. The vulnerability lies in the parser itself, not the Actix extractor.
Does middleBrick's XML bomb test disrupt my production API?
No. middleBrick sends a small payload that expands to only a few megabytes, which is non-disruptive for typical API servers. The scanner monitors responses for anomalies without causing crashes. However, we recommend scanning staging environments first to be cautious.