Email Injection in Actix with Hmac Signatures
Email Injection in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Email Injection in Actix occurs when user-controlled input is placed into email headers without strict validation or sanitization, and the endpoint is protected only by an Hmac Signature. In this scenario, an attacker can inject additional header lines (e.g., CC, BCC, or injected recipients) by supplying newline characters such as %0a or %0d%0a in parameters that are signed but not canonicalized before signing. The Hmac Signature verifies integrity of the signed payload, but if the payload is built by concatenating raw user input into email headers, the signature remains valid while the resulting message is altered in unexpected ways.
Consider an Actix web service that sends a confirmation email. The request body includes email, callback_url, and an Hmac signature in a header computed over a canonical string such as email|callback_url|timestamp. If the service places the email value directly into the To or Cc header and does not reject or encode newline characters, an attacker can provide an email like [email protected]%0a%0dcc: [email protected]. The Hmac signature will still verify because the canonical pre-hash string includes the raw input; however, the resulting email contains an additional recipient, enabling email injection.
This pattern is especially risky when the Hmac is computed over a subset of fields while other fields are appended to the email headers server-side. For example, if the signature covers the JSON body but the server then builds SMTP headers by interpolating the email field without normalization, the signed integrity guarantee does not protect the header construction phase. OWASP API Top 10 A03:2023 (Injection) and related email injection techniques map to this weakness, where control characters break the intended header structure.
In an unauthenticated scan, middleBrick would flag this as an input validation and data exposure concern: the endpoint reflects or sends messages to injected addresses, and sensitive information (e.g., internal relay details) might be exfiltrated via injected headers. The scanner tests whether newline characters in signed fields can alter the runtime behavior of email generation without invalidating the Hmac Signature.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
To remediate Email Injection in Actix while preserving Hmac Signature integrity, canonicalize and strictly validate all user-supplied data before it participates in both the signed payload and any email header construction. Use a two-stage approach: first, validate and normalize inputs; second, include only normalized values in the Hmac computation and header assembly.
In Actix web, define a handler that validates email addresses with a strict regex and rejects any input containing carriage return or line feed characters. Then, build the Hmac payload from the normalized values, and use a safe email library to construct headers so that user input never directly forms header lines.
use actix_web::{post, web, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac;
fn normalize_email(raw: &str) -> Result {
// Reject newlines and carriage returns to prevent header injection
if raw.contains('\n') || raw.contains('\r') {
return Err("invalid_email");
}
// Basic format check
let email = raw.trim().to_lowercase();
if email.contains('@') && email.contains('.') {
Ok(email)
} else {
Err("invalid_email")
}
}
fn build_hmac_signature(email: &str, callback_url: &str, timestamp: &str, key: &[u8]) -> String {
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size");
// Canonical order and no extra delimiters that could be manipulated
mac.update(email.as_bytes());
mac.update(b"|");
mac.update(callback_url.as_bytes());
mac.update(b"|");
mac.update(timestamp.as_bytes());
let result = mac.finalize();
hex::encode(result.into_bytes())
}
#[post("/confirm")]
async fn confirm(
form: web::Form,
secret: web::Data>,
) -> Result {
let email = normalize_email(&form.email)?;
let callback_url = normalize_url(&form.callback_url)?;
let timestamp = chrono::Utc::now().timestamp().to_string();
let signature = build_hmac_signature(&email, &callback_url, ×tamp, &secret);
// Compare signature from header (example) — in practice use constant-time compare
// Send email using a safe builder; user input is only in the address body, not raw header lines
let message = format!("To: {}\nSubject: Confirmation\n\nPlease confirm: {}", email, form.email);
// Assume send_mail is a function that uses a proper SMTP client library
send_mail(&message).map_err(|_| actix_web::error::ErrorInternalServerError("send failed"))?;
Ok(HttpResponse::Ok().body("Confirmation sent"))
}
fn normalize_url(raw: &str) -> Result {
if raw.contains('\n') || raw.contains('\r') {
return Err("invalid_url");
}
// Further normalization as needed
Ok(raw.trim().to_string())
}
In this example, the email field is validated and normalized before inclusion in the Hmac computation, ensuring that the signed canonical string and the email header content are aligned. The Hmac Signature covers the normalized email, preventing an attacker from injecting newline characters that would be accepted by the signature check but cause the server to append additional recipients.
Additionally, use a dedicated email library to construct headers instead of string concatenation. This prevents accidental injection even if future code changes inadvertently pass raw user input to header-building logic. middleBrick’s scans can verify that endpoints with Hmac protections still reject newline-injected values by testing with payloads such as victim%40example.com%0aCc%3a%20attacker%40evil.com and observing whether the signature validation and header parsing remain consistent.