Executive Summary
A critical vulnerability in Nodemailer’s process-global DNS caching mechanism allows attackers to intercept SMTP credentials in multi-tenant environments. The flaw stems from improper cache key construction that fails to account for TLS servername values, enabling attackers to manipulate DNS resolution and redirect victim credentials to attacker-controlled servers. All versions from 5.0.0 and above are affected. Organizations using Nodemailer in shared hosting or multi-tenant architectures should prioritize immediate patching and credential rotation.
What You Need to Know About This Vulnerability
How the DNS Cache Flaw Works
Nodemailer maintains a process-global DNS cache designed to improve performance by storing resolved hostnames. However, this cache uses only the host parameter as its key, while storing the associated TLS servername value alongside it. When multiple SMTP transports reference the same DNS host but specify different servername values, the first transport’s server name persists in the cache and gets reused by subsequent transports.
This design flaw creates a critical security gap: a malicious tenant can prime the DNS cache with their own server name, causing legitimate tenants’ SMTP connections to use the wrong SNI (Server Name Indication) value. The victim’s TLS handshake then succeeds against the attacker’s certificate, and the victim’s SMTP credentials are transmitted to the attacker’s server.
Why Multi-Tenant Environments Are at Highest Risk
Multi-tenant architectures—where multiple customers or users share the same application instance—face elevated risk because:
- Shared Process Space: All tenants’ Nodemailer transports operate within the same Node.js process, sharing the same DNS cache
- Attacker Control: Malicious tenants can deliberately create transports with specific
servernamevalues to poison the cache - Credential Exposure: SMTP credentials are typically tenant-specific and highly sensitive
- Difficult Detection: Cache poisoning attacks leave minimal audit trails and may go unnoticed
Single-tenant applications and those using separate processes per tenant have significantly lower exposure.
Real-World Attack Scenarios
Scenario 1: SaaS Email Platform Compromise
A multi-tenant SaaS platform allows customers to configure custom SMTP servers. Attacker Tenant A creates a transport pointing to mail.attacker.com with servername: "attacker.com". This primes the DNS cache. When Victim Tenant B attempts to send email through their configured SMTP server (mail.victim.com with servername: "victim.com"), the cached attacker servername is reused. The victim’s connection succeeds against the attacker’s certificate, and credentials are compromised.
Scenario 2: Shared Hosting Provider Attack
A hosting provider runs multiple customer applications on shared infrastructure. An attacker customer creates a Nodemailer transport with a malicious servername. When other customers’ applications attempt to send transactional emails, their SMTP credentials are intercepted, enabling account takeover and email spoofing.
Affected Versions and Products
Nodemailer Versions 5.0.0 and Above
Confirmed Vulnerable Versions:
- 5.0.0 (initial introduction of DNS caching)
- 10.0.1 (latest confirmed vulnerable in runtime testing)
- All intermediate versions from 5.0.0 through the latest unpatched release
Affected Ecosystem:
- Package: Nodemailer (npm registry)
- Repository: github.com/nodemailer/nodemailer
- Language: Node.js (JavaScript runtime)
Identifying If Your Application Is Vulnerable
Your application is at risk if:
- You use Nodemailer version 5.0.0 or later
- Your application operates in a multi-tenant environment
- Multiple tenants or users can create or configure SMTP transports
- Tenants share the same Node.js process (not isolated in separate processes)
- Different tenants use different SMTP servers with different
servernamevalues
Checking Your Current Nodemailer Version
Via package.json:
{
"dependencies": {
"nodemailer": "^6.9.0"
}
}
Via command line:
npm list nodemailer
Programmatically in Node.js:
const nodemailer = require('nodemailer');
const pkg = require('nodemailer/package.json');
console.log('Nodemailer version:', pkg.version);
Technical Breakdown of the Attack
How Attackers Prime the DNS Cache
The attack begins with cache initialization. When Attacker Tenant A creates an SMTP transport, Nodemailer’s code executes:
// Simplified Nodemailer DNS cache logic (vulnerable)
const dnsCache = {};
function getConnection(host, servername) {
if (!dnsCache[host]) {
dnsCache[host] = {
resolvedIP: dns.resolve(host),
servername: servername // Cache stores servername
};
}
return dnsCache[host];
}
The attacker creates a transport with:
const transport = nodemailer.createTransport({
host: 'mail.shared.com',
port: 465,
secure: true,
tls: {
servername: 'attacker.com' // Attacker's servername
}
});
This single call populates dnsCache['mail.shared.com'] with the attacker’s servername value.
The Servername Substitution Mechanism
When Victim Tenant B later creates their transport:
const victimTransport = nodemailer.createTransport({
host: 'mail.shared.com',
port: 465,
secure: true,
tls: {
servername: 'victim.com' // Victim's intended servername
}
});
Nodemailer retrieves the cached entry for mail.shared.com, which already contains servername: 'attacker.com'. The victim’s explicitly configured servername: 'victim.com' is ignored, and the attacker’s value is used instead.
How Credentials Reach the Wrong Server
The TLS handshake proceeds with the wrong SNI value:
- Victim’s Transport initiates connection to
mail.shared.com - DNS Resolution returns the legitimate IP (from cache)
- TLS Handshake sends SNI:
attacker.com(from cache, not victim’s config) - Attacker’s Server (listening on that IP or via DNS hijacking) presents a certificate for
attacker.com - Certificate Validation succeeds because the SNI matches the certificate
- SMTP Authentication proceeds, and victim’s credentials are transmitted to the attacker

Immediate Actions to Take
Upgrade Nodemailer to the Patched Version
Priority: Critical
Obtain the latest patched version from the Nodemailer repository:
npm update nodemailer
# or
npm install nodemailer@latest
Verify the update:
npm list nodemailer
For Production Environments:
- Test the patch in a staging environment first
- Plan a maintenance window for deployment
- Coordinate with your DevOps team to ensure zero-downtime deployment if possible
- Document the update in your change management system
Audit Your SMTP Configurations
Review all SMTP transports in your codebase:
# Search for Nodemailer transport creation
grep -r "createTransport" --include="*.js" .
grep -r "nodemailer" --include="*.js" .
For each transport, document:
- The
hostvalue - The
tls.servernamevalue - Which tenant or user owns the transport
- Whether multiple transports share the same host
Configuration Audit Checklist:
- [ ] Identify all Nodemailer transports in your application
- [ ] Map transports to tenants/users
- [ ] Verify each transport has an explicit
tls.servernamevalue - [ ] Confirm servername values match the actual SMTP server hostname
- [ ] Document any shared hosts across different tenants
Review Multi-Tenant Architecture for Exposure
Assess your application’s isolation model:
High Risk:
- Single Node.js process serving all tenants
- Shared SMTP host across multiple tenants
- Tenants can create or modify transport configurations
Medium Risk:
- Single process but isolated SMTP credentials per tenant
- Tenants cannot modify transport configurations
Low Risk:
- Separate Node.js process per tenant
- Isolated SMTP infrastructure per tenant
- Strict tenant isolation at the application level
Detection and Monitoring Strategies
Code Review Checklist for DNS Caching
Conduct a security-focused code review of your Nodemailer usage:
Review Items:
- [ ] Verify Nodemailer version is up-to-date
- [ ] Confirm all transports explicitly set
tls.servername - [ ] Check that servername values are tenant-specific and immutable
- [ ] Ensure transports are not created dynamically from user input
- [ ] Validate that SMTP credentials are not logged or exposed
- [ ] Review error handling for TLS failures
Code Review Example:
// VULNERABLE: servername not explicitly set
const transport = nodemailer.createTransport({
host: 'mail.example.com',
port: 465,
secure: true,
auth: { user, pass }
});
// SECURE: servername explicitly configured
const transport = nodemailer.createTransport({
host: 'mail.example.com',
port: 465,
secure: true,
tls: {
servername: 'mail.example.com' // Explicit and verified
},
auth: { user, pass }
});
Network Monitoring for Suspicious SNI Values
Implement network-level detection:
Monitor Outgoing SMTP Connections:
# Using tcpdump to capture TLS handshakes
tcpdump -i eth0 'tcp port 465 or tcp port 587' -w smtp_traffic.pcap
# Analyze with tshark
tshark -r smtp_traffic.pcap -Y tls.handshake.extensions_server_name \
-T fields -e tls.handshake.extensions_server_name
Detection Indicators:
- SNI value differs from the destination hostname
- Unexpected servername values in SMTP connections
- Connections to unauthorized SMTP servers
- Multiple different servername values for the same host
SIEM Integration:
Log and alert on:
- TLS handshake failures with certificate mismatches
- Unexpected SNI values in outgoing connections
- SMTP authentication failures followed by successful connections to different servers
Penetration Testing Approaches
Test Scenario 1: DNS Cache Poisoning Simulation
// Attacker tenant creates transport
const attackerTransport = nodemailer.createTransport({
host: 'mail.shared.com',
port: 465,
secure: true,
tls: { servername: 'attacker.com' }
});
// Victim tenant creates transport
const victimTransport = nodemailer.createTransport({
host: 'mail.shared.com',
port: 465,
secure: true,
tls: { servername: 'victim.com' }
});
// Test: Verify victim's SNI value in actual connection
// Expected: victim.com
// Vulnerable: attacker.com
Test Scenario 2: Multi-Tenant Isolation Testing
- Create multiple tenant transports with different servername values
- Monitor which servername is actually used in TLS handshakes
- Verify credentials are sent to the correct server
- Attempt to intercept credentials through cache manipulation
Test Scenario 3: Credential Interception Lab
Set up a controlled lab environment:
- Deploy a test application with vulnerable Nodemailer
- Create two tenant transports pointing to the same host
- Set up an attacker-controlled SMTP server
- Attempt to intercept victim credentials through cache poisoning
- Document findings and verify patch effectiveness
Immediate Actions to Take
Upgrade Nodemailer to the Patched Version
Priority: Critical
Obtain the latest patched version from the Nodemailer repository:
npm update nodemailer
# or
npm install nodemailer@latest
Verify the update:
npm list nodemailer
For Production Environments:
- Test the patch in a staging environment first
- Plan a maintenance window for deployment
- Coordinate with your DevOps team to ensure zero-downtime deployment if possible
- Document the update in your change management system
Audit Your SMTP Configurations
Review all SMTP transports in your codebase:
# Search for Nodemailer transport creation
grep -r "createTransport" --include="*.js" .
grep -r "nodemailer" --include="*.js" .
For each transport, document:
- The
hostvalue - The
tls.servernamevalue - Which tenant or user owns the transport
- Whether multiple transports share the same host
Configuration Audit Checklist:
- [ ] Identify all Nodemailer transports in your application
- [ ] Map transports to tenants/users
- [ ] Verify each transport has an explicit
tls.servernamevalue - [ ] Confirm servername values match the actual SMTP server hostname
- [ ] Document any shared hosts across different tenants
Review Multi-Tenant Architecture for Exposure
Assess your application’s isolation model:
High Risk:
- Single Node.js process serving all tenants
- Shared SMTP host across multiple tenants
- Tenants can create or modify transport configurations
Medium Risk:
- Single process but isolated SMTP credentials per tenant
- Tenants cannot modify transport configurations
Low Risk:
- Separate Node.js process per tenant
- Isolated SMTP infrastructure per tenant
- Strict tenant isolation at the application level
Long-Term Mitigation Strategies
Implementing Custom DNS Caching with Servername Keys
If you cannot immediately upgrade, implement a custom DNS cache that includes servername in the cache key:
const dns = require('dns').promises;
class SecureDNSCache {
constructor() {
this.cache = new Map();
this.ttl = 3600000; // 1 hour
}
// Cache key includes both host AND servername
getCacheKey(host, servername) {
return `${host}:${servername}`;
}
async resolve(host, servername) {
const key = this.getCacheKey(host, servername);
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.ip;
}
const ip = await dns.resolve4(host);
this.cache.set(key, {
ip: ip[0],
timestamp: Date.now()
});
return ip[0];
}
}
// Usage with Nodemailer
const dnsCache = new SecureDNSCache();
const transport = nodemailer.createTransport({
host: 'mail.example.com',
port: 465,
secure: true,
tls: {
servername: 'mail.example.com',
lookup: async (hostname, options, callback) => {
try {
const ip = await dnsCache.resolve(hostname, 'mail.example.com');
callback(null, ip, 4);
} catch (err) {
callback(err);
}
}
}
});
Tenant Isolation Best Practices
Process-Level Isolation:
// Use worker threads or child processes per tenant
const { Worker } = require('worker_threads');
function createTenantWorker(tenantId, config) {
const worker = new Worker('./tenant-worker.js');
worker.on('message', handleMessage);
worker.postMessage({ tenantId, config });
return worker;
}
Credential Management:
- Store SMTP credentials in a secure vault (HashiCorp Vault, AWS Secrets Manager)
- Rotate credentials regularly
- Use tenant-specific credentials, never shared
- Implement least-privilege access for SMTP accounts
Transport Isolation:
// Create transports in isolated contexts
class TenantEmailService {
constructor(tenantId, config) {
this.tenantId = tenantId;
this.transport = nodemailer.createTransport({
host: config.smtpHost,
port: config.smtpPort,
secure: true,
tls: {
servername: config.smtpHost,
rejectUnauthorized: true
},
auth: {
user: config.smtpUser,
pass: config.smtpPass
}
});
}
async sendMail(mailOptions) {
// Validate sender is within tenant's domain
if (!mailOptions.from.endsWith(this.tenantId)) {
throw new Error('Invalid sender for tenant');
}
return this.transport.sendMail(mailOptions);
}
}
TLS Configuration Hardening
Enforce Strict TLS Validation:
const transport = nodemailer.createTransport({
host: 'mail.example.com',
port: 465,
secure: true,
tls: {
servername: 'mail.example.com',
rejectUnauthorized: true, // Enforce certificate validation
minVersion: 'TLSv1.2', // Require TLS 1.2 or higher
ciphers: 'HIGH:!aNULL:!MD5' // Use strong ciphers only
}
});
Certificate Pinning (Advanced):
const fs = require('fs');
const tls = require('tls');
const transport = nodemailer.createTransport({
host: 'mail.example.com',
port: 465,
secure: true,
tls: {
servername: 'mail.example.com',
ca: [fs.readFileSync('./smtp-server-cert.pem')],
rejectUnauthorized: true
}
});
Regular Security Audits:
- [ ] Quarterly review of TLS configurations
- [ ] Annual penetration testing of email infrastructure
- [ ] Continuous monitoring of certificate validity
- [ ] Automated alerts for TLS handshake failures
Risk Assessment
Severity Evaluation
CVSS v3.1 Estimated Score: 8.1 (High)
Attack Vector: Network
Attack Complexity: Low
Privileges Required: Low (attacker needs tenant account)
User Interaction: None
Scope: Changed (affects other tenants)
Confidentiality Impact: High (SMTP credentials exposed)
Integrity Impact: Low
Availability Impact: None
Impact by Environment Type
| Environment | Risk Level | Impact |
|---|---|---|
| Single-tenant application | Low | Minimal; upgrade recommended |
| Multi-tenant SaaS | Critical | Credential disclosure, account takeover |
| Shared hosting | Critical | Cross-customer credential theft |
| Enterprise with process isolation | Medium | Reduced risk if tenants isolated |
FAQ
What is Nodemailer and why is it widely used?
Nodemailer is a popular Node.js module that simplifies email sending for applications. It provides a straightforward API for configuring SMTP connections, handling attachments, and managing email delivery. Its ease of use, reliability, and active maintenance make it a standard choice for developers building email functionality in Node.js applications, from transactional emails to marketing campaigns.
How does this vulnerability specifically affect my application?
If your application uses Nodemailer in a multi-tenant environment where multiple users or customers share the same SMTP host, attackers could intercept SMTP credentials from other tenants through DNS cache manipulation. An attacker with tenant access can prime the DNS cache with their own server name, causing legitimate tenants’ SMTP connections to use the wrong SNI value. This allows the attacker to intercept credentials and gain unauthorized access to email accounts.
Which versions of Nodemailer are vulnerable?
All versions from 5.0.0 and above are affected. Versions 5.0.0 and 10.0.1 have been confirmed vulnerable in runtime testing. The vulnerability was introduced when DNS caching was first implemented in version 5.0.0 and persists through all subsequent versions until patched.
What should I do if I’m using an affected version?
Immediately upgrade to the latest patched version of Nodemailer using npm update nodemailer. After upgrading, audit your SMTP configurations to ensure each transport has an explicit tls.servername value, and review your application architecture for potential credential exposure. If you operate a multi-tenant environment, also assess your tenant isolation model and consider implementing additional security controls.
Is this vulnerability only a concern for multi-tenant applications?
Yes, the vulnerability primarily affects multi-tenant environments where multiple tenants or users share the same Node.js process and can create or configure SMTP transports. Single-tenant applications have significantly lower risk because there is no opportunity for one user to poison the DNS cache for another. However, upgrading is still recommended as a security best practice.
How can I detect if this vulnerability has been exploited?
Monitor outgoing SMTP connections for unexpected SNI values that differ from the destination hostname. Review logs for connections to unauthorized SMTP servers or authentication failures followed by successful connections elsewhere. Conduct penetration testing to simulate the attack scenario and verify your security posture. Implement network monitoring to detect TLS handshake anomalies and certificate mismatches.
Can I implement a workaround without upgrading?
Yes, you can implement a custom DNS caching mechanism that includes the servername as part of the cache key, preventing different tenants’ servername values from colliding. Alternatively, you can isolate tenants to separate Node.js processes, preventing shared access to the same DNS cache. However, these are temporary mitigations—upgrading to the patched version is the recommended long-term solution.
What credentials are at risk?
SMTP authentication credentials (username and password) are the primary risk. These credentials grant access to email accounts and can be used for unauthorized email sending, account takeover, and further exploitation. In some cases, API keys or OAuth tokens used for SMTP authentication may also be exposed.
How should I rotate credentials after this vulnerability?
- Identify all SMTP accounts that may have been exposed
- Change passwords for all affected SMTP accounts
- Review email account activity logs for unauthorized access
- Revoke and reissue any API keys or OAuth tokens
- Monitor accounts for suspicious activity for 30 days post-rotation
- Notify affected users if their accounts were potentially compromised
Are there any known exploits in the wild?
As of the publication of this advisory, there are no confirmed reports of active exploitation in the wild. However, the relative simplicity of the attack and the widespread use of Nodemailer mean that exploitation is likely if the vulnerability remains unpatched. Prioritize patching immediately.
References
- Nodemailer GitHub Repository: github.com/nodemailer/nodemailer
- Node.js TLS Documentation: nodejs.org/api/tls.html
- OWASP Multi-Tenancy Security: owasp.org/www-community/attacks/Multi-Tenant_Attacks
- NIST Cybersecurity Framework: nist.gov/cyberframework
- CVE Mitre Database: cve.mitre.org
Advisory Published: [Current Date]
Last Updated: [Current Date]
Classification: Security Advisory – Critical
Distribution: Public
For questions or additional information, contact your security team or the Nodemailer maintainers through the official GitHub repository.