Email Injection in Actix
How Email Injection Manifests in Actix
Email injection in Actix applications typically occurs when user input is incorporated into email headers without proper validation. This vulnerability allows attackers to inject additional email headers or modify existing ones, potentially leading to spam distribution, phishing attacks, or information disclosure.
In Actix applications, email injection commonly appears in these scenarios:
- Password reset functionality where user-provided emails are used in SMTP commands
- Contact forms that send emails to administrators with user-supplied addresses
- Notification systems that include user data in email headers
- Registration flows that send confirmation emails
The vulnerability manifests when Actix handlers accept email addresses or other header fields from HTTP requests and pass them directly to email libraries like lettre or sendmail. An attacker can exploit this by providing specially crafted input containing newline characters ( ) to inject additional headers.
use actix_web::{web, HttpResponse, Responder};
use lettre::{SmtpClient, Message};
async fn contact_form(
form: web::Json,
) -> impl Responder {
let email = form.email.clone();
// Vulnerable: direct use of user input in email headers
let message = Message::builder()
.from(email.parse().unwrap())
.to("admin@example.com".parse().unwrap())
.subject("Contact Form Submission")
.body(form.message.clone())
.unwrap();
// SMTP client sends the message
let mut client = SmtpClient::new("smtp.example.com:587").unwrap();
client.send(message).await.unwrap();
HttpResponse::Ok().finish()
}
In this Actix handler, an attacker could submit an email like:
attacker@example.com%0d%0aX-Header: malicious-value
This would inject a new X-Header into the email, potentially bypassing spam filters or redirecting replies.
Actix-Specific Detection
Detecting email injection in Actix applications requires both static analysis and runtime scanning. middleBrick's API security scanner specifically tests for email injection vulnerabilities by examining how Actix applications handle email-related endpoints.
middleBrick scans Actix applications by:
- Identifying email-related endpoints through path analysis and parameter inspection
- Testing for newline injection in email fields using encoded characters like %0A and %0D
- Verifying proper email validation and sanitization
- Checking for direct header manipulation without validation
The scanner tests Actix applications with payloads designed to exploit email injection, including:
POST /contact HTTP/1.1
Host: example.com
Content-Type: application/json
{
"email": "test@example.com%0d%0aBcc: victim@example.com",
"message": "This is a test"
}
middleBrick analyzes the response and any outbound emails to detect if the injection succeeded. The scanner also examines Actix route handlers for patterns that indicate vulnerability, such as:
- Direct use of web::Json or web::Form without validation
- Lack of email format validation before SMTP operations
- Dynamic header construction from user input
- Missing sanitization of special characters
For Actix applications, middleBrick provides specific findings like:
Email Injection Vulnerability - High Severity
Endpoint: POST /api/contact
Risk: An attacker can inject additional email headers, potentially sending spam or phishing emails from your domain.
Recommendation: Validate and sanitize all email headers before sending. Use email validation libraries and encode special characters.
The scanner also checks for related issues like header injection in other protocols and improper error handling that might leak information about the email system configuration.
Actix-Specific Remediation
Remediating email injection in Actix applications requires a multi-layered approach. The most effective strategy combines strict input validation, proper email handling, and secure coding practices specific to Actix's ecosystem.
First, implement comprehensive email validation using Rust's email validation libraries:
use actix_web::{web, HttpResponse, Responder};
use lettre::{SmtpClient, Message};
use email_address::EmailAddress;
async fn secure_contact_form(
form: web::Json,
) -> impl Responder {
// Strict email validation
match EmailAddress::new(form.email.clone()) {
Ok(email) => {
// Valid email - proceed securely
let message = Message::builder()
.from("noreply@example.com".parse().unwrap())
.to(email.to_string().parse().unwrap())
.subject("Contact Form Submission")
.body(form.message.clone())
.unwrap();
let mut client = SmtpClient::new("smtp.example.com:587").unwrap();
client.send(message).await.unwrap();
HttpResponse::Ok().finish()
}
Err(_) => {
// Invalid email - reject request
HttpResponse::BadRequest().body("Invalid email format")
}
}
}
Second, use Actix's validation features to sanitize input before processing:
use actix_web::{web, HttpResponse, Responder};
use serde::Deserialize;
use validator::Validate;
#[derive(Deserialize, Validate)]
struct ContactForm {
#[validate(email)]
email: String,
message: String,
}
async fn validated_contact_form(
form: web::Json<ContactForm>,
) -> impl Responder {
// Validate using serde and validator
match form.validate() {
Ok(_) => {
// Proceed with secure email sending
send_email_securely(form.email.clone(), form.message.clone());
HttpResponse::Ok().finish()
}
Err(_) => HttpResponse::BadRequest().body("Invalid input data"),
}
}
Third, implement proper error handling to prevent information leakage:
async fn secure_email_handler(
form: web::Json<ContactForm>,
) -> impl Responder {
// Validate email format
if !is_valid_email(&form.email) {
return HttpResponse::BadRequest().body("Invalid email format");
}
// Sanitize input - remove newline characters
let sanitized_email = form.email.replace(["\r", "\n"], "");
// Use prepared email construction
let message = Message::builder()
.from("noreply@example.com".parse().unwrap())
.to(sanitized_email.parse().unwrap())
.subject("Contact Form Submission")
.body(form.message.clone())
.unwrap();
// Handle SMTP errors gracefully
match SmtpClient::new("smtp.example.com:587") {
Ok(mut client) => {
match client.send(message).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(_) => HttpResponse::InternalServerError().body("Email service unavailable")
}
}
Err(_) => HttpResponse::InternalServerError().body("Email service unavailable"),
}
}
Finally, consider using middleware in Actix to automatically validate email headers across all routes:
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, HttpMessage};
use actix_web::middleware::Transform;
use futures_util::future::{ready, Ready};
use std::task::{Context, Poll};
pub struct EmailInjectionProtection;
impl<S, B> Transform<S> for EmailInjectionProtection
where
S: actix_web::dev::Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
S::Future: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = actix_web::Error;
type InitError = ();
type Transform = EmailInjectionMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(EmailInjectionMiddleware { service }))
}
}
pub struct EmailInjectionMiddleware<S> {
service: S,
}
impl<S, B> actix_web::dev::Service for EmailInjectionMiddleware<S>
where
S: actix_web::dev::Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
S::Future: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = actix_web::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
// Check for email injection patterns in request body
if let Some(body) = req.extract_body() {
if contains_email_injection(&body) {
return ready(Err(actix_web::error::ErrorBadRequest("Potential email injection detected")));
}
}
self.service.call(req)
}
}
fn contains_email_injection(body: &str) -> bool {
// Check for newline characters in email fields
body.contains("\r") || body.contains("\n")
}
These remediation strategies, combined with regular scanning using middleBrick, provide comprehensive protection against email injection vulnerabilities in Actix applications.