Formula Injection in Actix with Basic Auth
Formula Injection in Actix with Basic Auth — how this specific combination creates or exposes the vulnerability
Formula Injection occurs when user-controlled data is interpreted as a formula by spreadsheet or document processors, leading to unwanted behavior such as executing commands or triggering side effects. In Actix web applications, combining Formula Injection vectors with HTTP Basic Authentication can expose sensitive workflows and authentication patterns that an attacker can probe or abuse.
When an Actix service accepts user input—such as a filename, export parameter, or report name—and later embeds that input into a generated spreadsheet (e.g., via CSV or XLSX generation), unsanitized input like =cmd|' /C calc'! or =HYPERLINK("javascript:alert(1)") can cause downstream applications to execute unintended actions. If the endpoint is protected only by Basic Auth without additional validation, an attacker may focus on authenticated flows where exported files are generated and downloaded, increasing the likelihood that a crafted file is opened in a privileged context.
Basic Auth in Actix is commonly implemented via middleware or extractor wrappers that parse the Authorization: Basic base64(credentials) header. If the service uses the credentials to scope data access (e.g., tenant or username derived from the identity) and then reflects user-supplied input into file generation without sanitization, the trust boundary between authentication and output creation becomes exploitable. For example, an authenticated user could supply a malicious export name that, when rendered in a CSV, executes a formula when opened in Excel, potentially leveraging the authenticated session’s permissions to access or export additional data.
The risk is amplified when the Actix endpoint returns files with predictable names or paths based on user input. An attacker without direct file access may use formula injection to trigger actions in the client environment, especially if the authenticated user opens the exported file without suspicion. Because Basic Auth transmits credentials with each request (base64-encoded, not encrypted), interception risks further compound the impact if formulas are used to exfiltrate data via embedded URLs pointing to attacker-controlled endpoints.
Basic Auth-Specific Remediation in Actix — concrete code fixes
Remediation focuses on input sanitization, output encoding, and strict separation between authentication context and generated content. Do not rely on Basic Auth alone to protect exported files; apply validation and encoding regardless of authentication method.
1. Validate and sanitize all user-controlled strings that may be used in generated files. Treat any user input that could be interpreted as a formula as untrusted.
2. Encode output appropriately for the target format. For CSV, escape leading formula characters (=, +, -, @) by prefixing with a single quote (') or encoding the value.
3. Use type-safe extractors and avoid deriving filenames or export parameters directly from user input.
Example: Secure Actix handler with Basic Auth and sanitized CSV export
use actix_web::{web, HttpResponse, HttpRequest, Error};
use actix_http::header::HeaderValue;
use base64::prelude::*;
/// Minimal Basic Auth extractor wrapper that returns the username if valid.
async fn extract_username(req: &HttpRequest) -> Option {
req.headers().get("Authorization")
.and_then(|h| h.to_str().ok())
.filter(|s| s.starts_with("Basic "))
.and_then(|s| {
let encoded = s.trim_start_matches("Basic ");
let decoded = BASE64_STANDARD.decode(encoded).ok()?;
let creds = String::from_utf8(decoded).ok()?;
let parts: Vec<&str> = creds.split(':').collect();
if parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty() {
Some(parts[0].to_string())
} else {
None
}
})
}
/// Sanitize user input to prevent formula injection in CSV.
/// If the value starts with '=', '+', '-', or '@', prefix with a single quote.
fn sanitize_csv_value(value: &str) -> String {
let trimmed = value.trim();
if trimmed.starts_with(|c: char| c == '=' || c == '+' || c == '-' || c == '@' || c == '\t') {
format!("'{}", trimmed)
} else {
trimmed.to_string()
}
}
async fn export_report(
req: HttpRequest,
query: web::Query>,
) -> Result {
let username = extract_username(&req)
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Invalid Basic Auth"))?;
let report_name = query.get("name")
.map(|s| sanitize_csv_value(s))
.unwrap_or_else(|| "report".to_string());
// Example CSV generation with sanitized values
let csv_content = format!("Username,Report\n{},{}", username, report_name);
let body = csv_content.into_bytes();
let mut resp = HttpResponse::Ok()
.content_type("text/csv; charset=utf-8")
.append_header(("Content-Disposition", format!("attachment; filename=\"{}\".csv", report_name)))
.body(body);
resp.headers_mut().insert(
"X-Content-Type-Options",
HeaderValue::from_static("nosniff"),
);
Ok(resp)
}
Key points in the example:
extract_usernameparses and validates the Basic Auth header without relying on external middleware assumptions.sanitize_csv_valueneutralizes formula injection by prefixing dangerous characters with a single quote, which Excel treats as plain text rather than a formula.- The filename in
Content-Dispositionuses the sanitized value to avoid path traversal or injection into the filename itself. - No assumptions are made about the authentication mechanism beyond the presence of a valid Basic Auth header; additional checks (e.g., TLS) should be enforced at the transport layer.
For higher assurance, consider moving away from Basic Auth in favor of token-based mechanisms where feasible, and apply strict allowlists for filenames and export parameters. The combination of runtime validation and context-aware output encoding significantly reduces the likelihood of formula exploitation in authenticated flows.