MEDIUM formula injectionaxum

Formula Injection in Axum

How Formula Injection Manifests in Axum

Formula Injection in Axum applications typically occurs when user-supplied data is included in CSV or spreadsheet exports without proper sanitization. This vulnerability allows attackers to inject malicious formulas that execute when the file is opened in spreadsheet applications like Microsoft Excel or Google Sheets.

In Axum, this often manifests in API endpoints that generate downloadable reports or data exports. The most common scenario involves endpoints that accept query parameters or request bodies containing data that gets embedded directly into CSV content.

Consider this vulnerable Axum endpoint:

use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::Response;

async fn export_users(Query(params): Query<HashMap<String, String>>) -> Result<Response, StatusCode> {
    let csv_content = format!("Name,Email,Department\n{},{},{} \n",
        params.get("name").unwrap_or(&String::new()),
        params.get("email").unwrap_or(&String::new()),
        params.get("department").unwrap_or(&String::new())
    );
    
    let response = axum::response::Response::builder()
        .header("Content-Type", "text/csv")
        .header("Content-Disposition", "attachment; filename=report.csv")
        .body(csv_content)
        .unwrap();
    
    Ok(response)
}

An attacker could craft a request with a malicious formula in the name parameter:

GET /export?name="=cmd|'/C calc'!A0&"&email=test@test.com&department=Sales

When opened in Excel, this formula could trigger the calculator application, demonstrating the potential for command execution.

Another Axum-specific manifestation occurs when using extractors that handle form data or JSON payloads for export functionality. The vulnerability becomes more severe when combined with Axum's powerful middleware system, where data flows through multiple processing stages before reaching the response generation.

Dynamic content generation in Axum's service handlers can also introduce formula injection if user input is concatenated into CSV templates without validation. This is particularly problematic when dealing with financial data, where formulas could manipulate calculations or extract sensitive information from other sheets.

Axum-Specific Detection

Detecting formula injection in Axum applications requires a combination of static analysis and runtime scanning. For static detection, examine all endpoints that generate downloadable content, particularly those using CSV or other spreadsheet formats.

Using middleBrick's CLI tool, you can scan your Axum API for formula injection vulnerabilities:

npx middlebrick scan http://localhost:3000 --category=input-validation

middleBrick specifically tests for formula injection patterns by submitting payloads containing common formula syntax like:

  • =1+1 - Basic formula detection
  • ++ - Formula initiation characters
  • @ - Formula reference characters
  • cmd|'/C - Windows command execution
  • HYPERLINK - URL-based attacks

The scanner analyzes the HTTP response to detect if these payloads are reflected in the output without sanitization.

For runtime detection in development, implement logging middleware that tracks suspicious patterns in export endpoints. Here's an example Axum middleware for formula injection detection:

use axum::middleware::Next;
use axum::response::Response;
use axum::http::Request;

async fn formula_injection_middleware(
    req: Request<Body>,
    next: Next<Body>
) -> Result<Response, axum::http::Error> {
    let path = req.uri().path();
    
    if path.contains("/export") || path.contains("/download") {
        let query = req.uri().query().unwrap_or("");
        
        if query.contains("=") || query.contains("++") || query.contains("@") {
            log::warn!("Suspicious formula injection pattern detected: {}", query);
        }
    }
    
    next.run(req).await
}

Additionally, use unit tests with formula injection payloads to verify your sanitization logic:

#[tokio::test]
async fn test_formula_injection_protection() {
    let app = export_users_route();
    
    let malicious_input = "=1+1&";
    let request = Request::get(
        "/export?name=John&email=john@example.com&department="
    ).body(malicious_input).unwrap();
    
    let response = app.call(request).await;
    assert!(response.status().is_success());
    
    let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
    let csv_content = String::from_utf8(body.to_vec()).unwrap();
    
    assert!(!csv_content.contains("=1+1"));
}

Axum-Specific Remediation

Remediating formula injection in Axum requires a defense-in-depth approach. The primary strategy is input sanitization before CSV generation. Here's a comprehensive solution using Axum's built-in features:

use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::Response;
use axum::response::Html;

fn sanitize_for_csv(input: &str) -> String {
    let mut result = String::new();
    let chars: Vec<char> = input.chars().collect();
    
    for (i, ch) in chars.iter().enumerate() {
        match ch {
            '=' | '+' | '-' | '@' | '`' | '|' | '\'' if i == 0 => {
                result.push_str("\'");
                result.push(*ch);
            }
            _ => result.push(*ch),
        }
    }
    
    result
}

async fn secure_export_users(Query(params): Query<HashMap<String, String>>) 
    -> Result<Response, StatusCode> 
{
    let sanitized_name = sanitize_for_csv(params.get("name").unwrap_or(&String::new()));
    let sanitized_email = sanitize_for_csv(params.get("email").unwrap_or(&String::new()));
    let sanitized_department = sanitize_for_csv(params.get("department").unwrap_or(&String::new()));
    
    let csv_content = format!("Name,Email,Department\n{},{},{} \n",
        sanitized_name,
        sanitized_email,
        sanitized_department
    );
    
    let response = axum::response::Response::builder()
        .header("Content-Type", "text/csv")
        .header("Content-Disposition", "attachment; filename=report.csv")
        .body(csv_content)
        .unwrap();
    
    Ok(response)
}

The sanitization function escapes formula initiation characters by prefixing them with a single quote, which Excel treats as a literal character rather than formula syntax.

For more robust protection, implement a CSV generation middleware that automatically sanitizes all user-supplied data:

use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use axum::http::StatusCode;

#[derive(Debug)]
struct SanitizedParams(HashMap<String, String>);

#[async_trait]
impl FromRequestParts<axum::http::Request<axum::body::Body>> for SanitizedParams {
    type Rejection = StatusCode;

    async fn from_request_parts(parts: &mut Parts, req: &mut axum::http::Request<axum::body::Body>) 
        -> Result<Self, Self::Rejection> 
    {
        let query = axum::extract::Query::>::from_request_parts(parts, req).await.map_err(|_| StatusCode::BAD_REQUEST)?;
        
        let sanitized = query.0.into_iter()
            .map(|(k, v)| (k, sanitize_for_csv(&v)))
            .collect();
            
        Ok(SanitizedParams(sanitized))
    }
}

async fn export_with_sanitization(SanitizedParams(params): SanitizedParams) 
    -> Result<Response, StatusCode> 
{
    // Safe to use params directly
    let csv_content = generate_csv(&params);
    
    // ... response creation
    Ok(create_csv_response(csv_content))
}

Additionally, configure your Axum application to use Content Security Policy headers for any HTML-based reporting interfaces that might be related to the export functionality:

use axum::middleware::Next;
use axum::response::Response;
use axum::http::Request;

async fn csp_middleware(
    req: Request<Body>,
    next: Next<Body>
) -> Result<Response, axum::http::Error> {
    let mut response = next.run(req).await?;
    
    if let Some(headers) = response.headers_mut() {
        headers.insert("X-Content-Type-Options", "nosniff".parse().unwrap());
        headers.insert("X-XSS-Protection", "1; mode=block".parse().unwrap());
    }
    
    Ok(response)
}

Frequently Asked Questions

How does formula injection differ from CSV injection?
Formula injection is a specific type of CSV injection that targets formula syntax. While CSV injection encompasses any malicious content in CSV files (including macros or embedded objects), formula injection specifically exploits spreadsheet formula evaluation engines. The key distinction is that formula injection relies on the spreadsheet application interpreting certain character sequences as executable formulas rather than plain text.
Can middleBrick detect formula injection in Axum applications?
Yes, middleBrick can detect formula injection vulnerabilities in Axum applications through its input validation category scans. The tool tests endpoints with formula injection payloads and analyzes responses for successful reflection without sanitization. For Axum specifically, middleBrick examines endpoints that generate downloadable content and reports on any detected formula injection patterns, providing severity ratings and remediation guidance.