Email Injection in Chi with Mutual Tls
Email Injection in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability
Email Injection is a web application security issue where untrusted input is inserted into email headers, enabling header manipulation, spam relay, or phishing. In the Chi web framework for Clojure, routes typically extract data from requests (e.g., JSON bodies, query params, headers) and pass it to email-sending logic. When developers use libraries such as aleph or buddy for SMTP or TLS, they may inadvertently construct email headers using unchecked user input without proper sanitization or validation. Mutual Transport Layer Security (Mutual TLS) authenticates both client and server using certificates, which secures the transport channel but does not affect application-layer validation. Therefore, enabling Mutual TLS in Chi improves identity assurance and encryption, yet it does not prevent an attacker from injecting newline characters into headers if the application accepts and uses attacker-controlled data in email fields like From, Reply-To, or Subject.
Consider a Chi endpoint that receives a JSON payload with a user-controlled :email field and uses it directly in an email header. Even with Mutual TLS enforced on the server side, the route handler might do the following:
(defn send-welcome-email [recipient-name recipient-email])
;; naive header construction — vulnerable to injection
(let [subject (str "Welcome, " recipient-name)
headers {"From" "noreply@example.com"
"To" recipient-email
"Subject" subject}]
(smtp/send-email {:host "smtp.example.com"
:port 587
:tls? true
:credentials {:username "smtp-user" :password "**"}
:headers headers})))
If recipient-email contains a newline (e.g., attacker@example.com\r\nCc: spam@example.com), the SMTP client may interpret the injected line as an additional header, leading to unintended recipients or header smuggling. Mutual TLS ensures the client presenting the certificate is trusted, but it does not validate or sanitize the content of recipient-email. Thus, the combination of Chi routes that accept untrusted input and SMTP libraries that directly use that input in headers creates a pathway for Email Injection. Attackers can exploit this to spoof sender identity, escalate phishing, or bypass rate limits if the application relies on email-based controls.
Chi applications commonly rely on middleware to enforce Mutual TLS, which provides strong peer authentication and confidentiality. However, security checks such as header validation must be implemented explicitly in route handlers or via dedicated libraries. The presence of Mutual TLS can give a false sense of overall security, leading developers to overlook injection risks in email workflows. This is particularly relevant when integrating with third-party email services or when handling user registration and contact forms where input is not strictly validated against a safe pattern.
Mutual Tls-Specific Remediation in Chi — concrete code fixes
To remediate Email Injection in Chi when Mutual TLS is used, focus on input validation, header construction, and safe encoding. Do not rely on Mutual TLS to sanitize user input. Instead, validate email addresses with a strict allowlist pattern, avoid direct concatenation for headers, and use libraries that support safe header building. Below are concrete code examples demonstrating secure practices.
1. Validate email input using a robust pattern and normalize before use:
(ns myapp.email
(:require [clojure.string :as str]
[malli.core :as m])
(def email-regex #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
(defn valid-email? [email]
(and (string? email)
(re-matches email-regex email)))
(defn normalize-email [email]
(str/lower-case (str/trim email)))
2. Construct email headers safely by avoiding raw user input in header values that could contain newlines. Use a library-friendly map and encode where necessary:
(ns myapp.routes
(:require [compojure.core :refer [POST]]
[myapp.email :refer [valid-email? normalize-email]]))
(POST "/register" req
(let [body (:body req)
email (get body "email")
name (get body "name")]
(if (and (valid-email? email)
(not (str/blank? name)))
(let [safe-email (normalize-email email)
subject (str "Welcome, " (str/trim name))
headers {"From" "noreply@example.com"
"To" safe-email
"Subject" (MIME/encode "utf-8" "Q" subject)}] ;; use MIME encoding for subject
(smtp/send-email {:host "smtp.example.com"
:port 587
:tls? true
:credentials {:username "smtp-user" :password "**"}
:headers headers}))
{:status 400 :body "Invalid input"})))
3. Enforce Mutual TLS at the server level using Chi middleware, ensuring certificates are verified independently of application logic:
(ns myapp.server
(:require [cheshire.core :as json]
[compojure.core :refer [defroutes GET POST]]
[compojure.route :as route]
[ring.adapter.jetty :as jetty]
[ring.middleware.ssl :refer [wrap-ssl]]))
;; Example Mutual TLS enforcement via middleware (implementation-specific)
(defn enforce-client-cert [handler]
(fn [request]
(if (some? (:client-cert request))
(handler request)
{:status 401 :body "Client certificate required"})))
(defroutes app-routes
(POST "/api/email" [] (fn [req] (/* route handler */)))
(route/not-found "Not Found"))
(def app
(-> app-routes
enforce-client-cert
wrap-ssl))
By combining strict validation, safe header construction, and proper use of Mutual TLS, Chi applications can mitigate Email Injection while maintaining secure transport. Remember that Mutual TLS secures the channel, but application-layer hygiene remains essential to prevent header manipulation.