Email Injection with Hmac Signatures
How Email Injection Manifests in Hmac Signatures
Email injection in HMAC signature contexts typically occurs when attackers manipulate email headers within signed payloads. This attack exploits the trust placed in cryptographic signatures by injecting additional email headers that bypass validation.
The most common scenario involves API endpoints that process email notifications or transactional emails where the HMAC signature covers the email content. Attackers can append additional headers like CC:, BCC:, or Subject: by injecting newline characters (
or
) into input fields.
# Vulnerable HMAC email processing
import hmac
import hashlib
def process_email(email_payload, signature, secret_key):
# Verify HMAC signature
computed_sig = hmac.new(secret_key, email_payload.encode(), hashlib.sha256).hexdigest()
if computed_sig != signature:
return 'Invalid signature'
# Process email - vulnerable to injection
headers, body = email_payload.split('\n\n', 1)
# Attacker can inject: "Subject: Hello\nCC: victim@example.com\n\nOriginal content"
return headers, bodyThe attack works because the HMAC signature validates the entire payload, but the email parser processes the injected headers as legitimate. This allows attackers to redirect emails, exfiltrate data through CC/BCC fields, or modify email content while maintaining a valid signature.
Another manifestation occurs in webhook verification systems where HMAC-signed payloads contain email addresses. Attackers can exploit insufficient validation of email header formatting within the signed content.
// Vulnerable Node.js HMAC verification
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto.createHmac('sha256', secret)
.update(body)
.digest('hex');
if (expected !== signature) {
return false;
}
// Parse email from JSON body - vulnerable to injection
const email = JSON.parse(body).email;
// If email contains \nCC: attacker@evil.com, headers get injected
sendEmail(email);
}Attackers specifically target the boundary between the HMAC-verified content and the email processing logic, knowing that cryptographic validation doesn't prevent logical injection attacks within the signed payload structure.
HMAC Signatures-Specific Detection
Detecting email injection in HMAC-signed systems requires analyzing both the cryptographic verification and the payload processing logic. middleBrick's API security scanner specifically tests for this vulnerability through several detection methods.
The scanner first identifies HMAC signature verification patterns in your API endpoints, then tests for email header injection by submitting payloads containing newline characters and additional email headers. It verifies whether the injected headers are processed despite the valid HMAC signature.
# Using middleBrick CLI to scan for email injection in HMAC endpoints
npm install -g middlebrick
middlebrick scan https://api.example.com/webhook \
--test-email-injection \
--hmac-secret test-secret \
--output jsonmiddleBrick tests 12 security categories including authentication bypasses and input validation. For HMAC email injection specifically, it:
- Detects HMAC signature verification code patterns
- Attempts newline injection with email header manipulation
- Verifies if injected headers are processed by the email system
- Checks for insufficient email header validation
- Tests boundary conditions between signature verification and payload processing
- Analyzes OpenAPI specs for email-related endpoints
- Attempts multiple injection patterns across different email header formats
The scanner provides a security score (0-100) with letter grades and identifies this as a critical vulnerability when found, mapping it to OWASP API Top 10 risks including broken authentication and improper input validation.
HMAC Signatures-Specific Remediation
Remediating email injection in HMAC-signed systems requires both cryptographic and input validation improvements. The key is to validate email content before and after HMAC verification, and to sanitize email headers properly.
First, implement strict email header validation that removes or escapes newline characters before processing:
import re
import hmac
import hashlib
def sanitize_email_headers(email_payload):
# Remove any newline characters in email headers
lines = email_payload.split('\n')
sanitized = []
for line in lines:
if ':' in line:
# Header line - remove newlines
header, value = line.split(':', 1)
value = re.sub(r'[\r\n]', '', value)
sanitized.append(f"{header}: {value}")
else:
sanitized.append(line)
return '\n'.join(sanitized)
def secure_process_email(email_payload, signature, secret_key):
# Sanitize first
email_payload = sanitize_email_headers(email_payload)
# Verify HMAC
computed_sig = hmac.new(secret_key, email_payload.encode(), hashlib.sha256).hexdigest()
if computed_sig != signature:
return 'Invalid signature'
# Safe to process headers
headers, body = email_payload.split('\n\n', 1)
return headers, bodyFor Node.js applications using Express, implement middleware that validates email format and structure:
const crypto = require('crypto');
const validator = require('email-validator');
function validateEmailPayload(payload) {
// Check for newline injection in email fields
const emailPattern = /[^\s@]+@[^\s@]+\.[^\s@]+/g;
const matches = payload.match(emailPattern);
if (matches) {
for (const email of matches) {
if (!validator.validate(email)) {
return false;
}
}
}
// Check for suspicious header patterns
if (payload.includes('\nCC:') || payload.includes('\nBCC:') ||
payload.includes('\nSubject:') || payload.includes('\nTo:')) {
// Only allow these headers in expected positions
const headerLines = payload.split('\n');
for (let i = 0; i < headerLines.length; i++) {
const line = headerLines[i];
if (line.startsWith('CC:') || line.startsWith('BCC:') ||
line.startsWith('Subject:') || line.startsWith('To:')) {
if (i === 0 || i > 5) { // Headers should be in first 5 lines
return false;
}
}
}
}
return true;
}
function verifyWebhook(body, signature, secret) {
if (!validateEmailPayload(body)) {
return false;
}
const expected = crypto.createHmac('sha256', secret)
.update(body)
.digest('hex');
return expected === signature;
}Additionally, implement Content Security Policy headers and use email libraries that automatically sanitize headers. For Python, use the email library's built-in validation:
from email.parser import Parser
import hmac
import hashlib
def parse_email_securely(email_payload, signature, secret_key):
# Verify signature first
computed_sig = hmac.new(secret_key, email_payload.encode(), hashlib.sha256).hexdigest()
if computed_sig != signature:
return None
# Parse with email library - automatically handles header injection
parser = Parser()
try:
msg = parser.parsestr(email_payload)
return msg
except Exception:
return None