Email Injection in Feathersjs with Hmac Signatures
Email Injection in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Email Injection is a class of injection flaws where untrusted data supplied as email input is used to manipulate the behavior of an email-related system, such as headers or the SMTP protocol flow. In Feathersjs, a common pattern is to send transactional email notifications (for example, account confirmation or password resets) based on user-controlled data such as email addresses or user IDs. If these values are concatenated into email headers or message bodies without validation, an attacker can inject additional headers or control message routing.
When Hmac Signatures are used in Feathersjs, they are typically implemented to ensure integrity and authenticity of requests or to sign tokens used by email services (e.g., password reset tokens). The vulnerability arises when the data used to compute or verify the Hmac is derived from or influenced by user-controlled email input. For example, if the signature is computed over a string that includes the email address, and that email address is later used unsafely in an email header, the Hmac does not protect against injection—it only ensures that the signed payload has not been altered. An attacker can still inject newline characters (e.g., %0a or \r\n) into the email field to inject extra headers such as Cc: or Bcc:, leading to information disclosure or email spoofing.
Consider a Feathersjs service that sends a reset email. The server may create a signed token using an Hmac, embed the user’s email in the token or in a redirect URL, and then include that email in the To or Reply-To header. If the email value is not strictly validated and sanitized, an attacker can supply a payload like [email protected]%0aCc:%[email protected]. Even if the token’s Hmac is valid, the injected header can cause the email to be sent to an unintended recipient, bypassing intended routing logic. This is an injection issue, not a signature forgery issue, because the signature may still verify while the message routing is compromised.
Moreover, if the Hmac is used to sign configuration values (such as allowed redirect URLs for email links), and the email address is part of the signed context, an attacker might leverage injection to manipulate the resulting link behavior. For instance, newline injection in query parameters can break URL parsing and lead to open redirects or unintended pre-filled headers in email templates. OWASP API Top 10 category A1 (Broken Object Level Authorization) and A3 (Injection) are relevant when untrusted input influences both authorization decisions and email composition.
In practice, this specific combination means developers might assume Hmac Signatures alone prevent abuse of email fields, but signatures do not sanitize or validate content. The email channel remains vulnerable to classic injection techniques such as header splitting. Real-world examples include CVE scenarios where injected headers lead to phishing or spam amplification. Therefore, validation of email format and strict separation of signing logic from header construction is essential to prevent abuse.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on strict input validation, avoiding concatenation of user input into email headers, and ensuring Hmac usage is limited to integrity checks without assuming security against injection.
- Validate and sanitize email input: Use a strict allowlist for email format and reject any control characters or newline sequences. For example:
const validator = require('validator');
app.service('users').hooks({
before: {
create: [context => {
if (!validator.isEmail(context.data.email)) {
throw new Error('Invalid email format');
}
// Ensure no newline or carriage return characters
if (/[\r\n]/.test(context.data.email)) {
throw new Error('Invalid characters in email');
}
return context;
}]
}
});
- Do not inject user input into email headers: Build headers programmatically using a mail library that enforces safe header separation. For example, using Nodemailer:
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({ /* SMTP config */ });
function sendResetEmail(toEmail, token) {
const mailOptions = {
from: '[email protected]',
to: toEmail, // toEmail must already be validated
subject: 'Password Reset',
text: `Reset link: https://app.example.com/reset?token=${token}`
};
return transporter.sendMail(mailOptions);
}
- Use Hmac for integrity, not for sanitization: Compute and verify Hmac on canonical, validated data only. For example, signing a verified email and a timestamp:
const crypto = require('crypto');
function generateSignedToken(email, timestamp, secret) {
const payload = JSON.stringify({ email: email, ts: timestamp });
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
return { token: hmac.digest('hex'), payload };
}
function verifySignedToken(token, payload, secret) {
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
return timingSafeEqual(token, expected);
}
// Safe usage: only after email validation
const { token, payload } = generateSignedToken(validatedEmail, Date.now(), process.env.HMAC_SECRET);
// Store or send token; use validatedEmail separately for headers
- Separate concerns: sign identifiers, not headers: If you need to include user references in URLs sent via email, use opaque tokens mapped server-side rather than embedding raw identifiers that could be manipulated. Example token generation and verification:
const crypto = require('crypto');
function createTokenForEmail(email) {
const iv = crypto.randomBytes(16);
const hmac = crypto.createHmac('sha256', process.env.HMAC_SECRET);
hmac.update(email + iv.toString('hex'));
return iv.toString('hex') + ':' + hmac.digest('hex');
}
function verifyTokenForEmail(token, email) {
const [iv, sig] = token.split(':');
const hmac = crypto.createHmac('sha256', process.env.HMAC_SECRET);
hmac.update(email + iv);
const expected = hmac.digest('hex');
return timingSafeEqual(sig, expected);
}
// In Feathers hook: generate token for validated email; do not place raw email in headers
These steps ensure that Hmac Signatures provide integrity for the signed payload while email headers remain safe from injection. Defense in depth includes input validation, safe mail library usage, and avoiding the assumption that cryptographic integrity alone prevents injection.