Loading learning content...
When you compose an email and click "Send," a sophisticated multi-stage process begins. The first critical step—mail submission—involves your email client securely authenticating with your mail server and handing off the message for delivery. This process, often invisible to users, is where security policies are enforced, sender identities are verified, and messages enter the global email infrastructure.
Mail submission might seem simple—just sending data to a server. But understanding its nuances is essential for system administrators configuring mail servers, developers building email-sending applications, and security professionals protecting against email-based attacks. Misconfigured submission is a common source of deliverability problems and security vulnerabilities.
By the end of this page, you will understand the critical distinction between mail submission and mail relay, master the role of port 587 and the Mail Submission Agent (MSA), comprehend SMTP authentication mechanisms (AUTH command), and learn modern security best practices including mandatory TLS and proper client configuration.
SMTP serves two fundamentally different purposes, and understanding this distinction is crucial for proper email architecture:
Mail Submission: The process by which an end-user's email client (MUA) transfers a message to their organization's mail server for delivery. This is a trusted transaction between an authenticated user and their own mail provider.
Mail Relay: The process by which one mail server (MTA) transfers a message to another mail server, either as an intermediate hop or for final delivery. This is a potentially untrusted transaction between servers that may not have prior relationship.
| Aspect | Mail Submission | Mail Relay |
|---|---|---|
| Participants | Email client (MUA) → Submission server (MSA) | Mail server (MTA) → Mail server (MTA) |
| Standard Port | 587 (submission) or 465 (implicit TLS) | 25 (SMTP) |
| Authentication | Required (username/password or certificate) | Rarely required; relies on IP/domain trust |
| Authorization | User authorized to send as their identity | Server authorized to relay for domain |
| Encryption | Mandatory TLS (modern requirement) | Opportunistic STARTTLS or mandatory via MTA-STS |
| Rate Limiting | Per-user quotas common | Per-server/IP reputation |
| Example | Your Outlook connects to smtp.yourcompany.com | smtp.yourcompany.com connects to gmail-smtp.l.google.com |
Why the Distinction Matters:
Historically, port 25 handled both submission and relay. This created severe security problems:
RFC 6409 formalized the separation, establishing port 587 specifically for authenticated mail submission. This solved multiple problems:
A mail server should NEVER accept unauthenticated mail submission on port 587. Conversely, it should only accept relay traffic on port 25 from authorized sources (its own users' mail, or domains it explicitly serves). Violating these principles creates open relays.
The Mail Submission Agent (MSA) is the server component responsible for receiving email from authenticated clients. While often implemented by the same software as the MTA (like Postfix or Exchange), the MSA has distinct responsibilities and security requirements.
MSA Responsibilities:
Header Correction and Addition:
Unlike MTAs in relay mode, the MSA actively modifies the message to ensure compliance:
Received: from [192.168.1.50] (client.example.org [192.168.1.50])
by mail.example.com (MSA) with ESMTPSA id ABC123
for <recipient@example.org>;
Mon, 15 Jan 2024 10:30:00 -0500
MSA vs. MTA Implementation:
In most mail servers like Postfix, the MSA and MTA are the same software configured differently:
A well-configured MSA prevents sender spoofing by verifying that the authenticated user is authorized to send from the claimed address. For example, if Alice authenticates, she should only be allowed to send with From: addresses she owns or has delegation for.
The AUTH command, defined in RFC 4954, enables SMTP clients to authenticate with servers. Authentication is mandatory for mail submission and is how servers know which user is sending and whether to accept their messages.
AUTH Command Syntax:
AUTH mechanism [initial-response]
The server advertises supported mechanisms in its EHLO response:
250-AUTH LOGIN PLAIN CRAM-MD5 XOAUTH2
Common Authentication Mechanisms:
| Mechanism | Security | Description | Use Case |
|---|---|---|---|
| PLAIN | Weak (needs TLS) | Base64-encoded username and password in single response | Most common; simple but requires TLS |
| LOGIN | Weak (needs TLS) | Base64-encoded username and password in separate prompts | Legacy; similar security to PLAIN |
| CRAM-MD5 | Medium | Challenge-response using MD5 hash; password not transmitted | Avoids sending password, but MD5 is weak |
| XOAUTH2 | Strong | OAuth 2.0 access token authentication | Google, Microsoft; token-based, no passwords |
| OAUTHBEARER | Strong | RFC 7628 OAuth 2.0 bearer token | Standardized OAuth for SMTP |
| SCRAM-SHA-256 | Strong | Salted Challenge Response with SHA-256 | Modern, secure password-based auth |
AUTH PLAIN Example:
The most common mechanism due to its simplicity. Credentials are Base64-encoded but not encrypted—TLS is mandatory.
# Credentials: username = alice, password = secretpass
# Format: \\0username\\0password (NUL-separated)
# Base64(\"\\0alice\\0secretpass\") = AGFsaWNlAHNlY3JldHBhc3M=
C: AUTH PLAIN AGFsaWNlAHNlY3JldHBhc3M=
S: 235 2.7.0 Authentication successful
AUTH LOGIN Example:
A multi-step mechanism where server prompts for username and password separately:
C: AUTH LOGIN
S: 334 VXNlcm5hbWU6 (Base64 for "Username:")
C: YWxpY2U= (Base64 for "alice")
S: 334 UGFzc3dvcmQ6 (Base64 for "Password:")
C: c2VjcmV0cGFzcw== (Base64 for "secretpass")
S: 235 2.7.0 Authentication successful
1234567891011121314151617181920212223242526272829303132333435363738394041
# Complete submission session with AUTH PLAINC: EHLO client.example.orgS: 250-mail.example.comS: 250-STARTTLSS: 250-AUTH LOGIN PLAIN CRAM-MD5S: 250 SIZE 52428800 # Upgrade to TLS first (credentials would be exposed otherwise!)C: STARTTLSS: 220 2.0.0 Ready to start TLS # ... TLS handshake ... # Re-issue EHLO after TLS (required)C: EHLO client.example.orgS: 250-mail.example.comS: 250-AUTH LOGIN PLAIN CRAM-MD5S: 250 SIZE 52428800 # Authenticate with PLAIN mechanism# Credentials: alice:secretpass# Base64("\0alice\0secretpass") = AGFsaWNlAHNlY3JldHBhc3M=C: AUTH PLAIN AGFsaWNlAHNlY3JldHBhc3M=S: 235 2.7.0 Authentication successful # Now send mail as authenticated userC: MAIL FROM:<alice@example.org>S: 250 2.1.0 OKC: RCPT TO:<bob@example.com>S: 250 2.1.5 OKC: DATAS: 354 Start mail inputC: From: Alice <alice@example.org>C: To: Bob <bob@example.com>C: Subject: Authenticated messageC:C: This message was sent via authenticated submission.C: .S: 250 2.0.0 Message acceptedC: QUITS: 221 2.0.0 ByeAUTH PLAIN and AUTH LOGIN transmit credentials in Base64 encoding—which is trivially decodable, NOT encryption. Without TLS, credentials are visible to any network observer. Always use STARTTLS before AUTH, or connect directly to port 465 (implicit TLS).
Traditional username/password authentication has significant limitations for mail submission: password management burden, reuse risks, lack of granular permissions, and difficulty revoking access. Modern providers increasingly require or prefer OAuth 2.0 authentication.
XOAUTH2 Mechanism:
Google introduced XOAUTH2 for SMTP authentication, now adopted by Microsoft and others:
# OAuth2 token format for SMTP
# Base64("user=email\x01auth=Bearer token\x01\x01")
C: AUTH XOAUTH2 dXNlcj1hbGljZUBnbWFpbC5jb20BYXV0aD1CZWFyZXIgeWEyOS5...
S: 235 2.7.0 Authentication successful
How XOAUTH2 Works:
Constructing the XOAUTH2 String:
import base64
user = 'alice@gmail.com'
access_token = 'ya29.a0AfH6SMB...' # OAuth access token
# Format: "user={email}\x01auth=Bearer {token}\x01\x01"
auth_string = f'user={user}\x01auth=Bearer {access_token}\x01\x01'
encoded = base64.b64encode(auth_string.encode()).decode()
print(f'AUTH XOAUTH2 {encoded}')
Benefits of OAuth for Email:
Google and Microsoft now require OAuth (or "app passwords") for SMTP access from third-party applications. Traditional password authentication is disabled or restricted for security accounts. Plan to implement OAuth in any production email-sending application.
Encryption is non-negotiable for mail submission. Without TLS, credentials, message content, and metadata are visible to anyone who can observe network traffic. Modern email security mandates TLS for all submission traffic.
Two TLS Models:
1. STARTTLS (Port 587) — Opportunistic Upgrade
C: [TCP connect to port 587]
S: 220 mail.example.com ESMTP
C: EHLO client.example.org
S: 250-STARTTLS
S: 250 OK
C: STARTTLS
S: 220 Ready to start TLS
[TLS handshake]
C: EHLO client.example.org (re-issued over TLS)
2. Implicit TLS (Port 465) — Immediate Encryption
C: [TCP + TLS connect to port 465]
S: 220 mail.example.com ESMTP
C: EHLO client.example.org
With implicit TLS, the connection is encrypted from the first byte—no plaintext phase exists.
| Aspect | STARTTLS (Port 587) | Implicit TLS (Port 465) |
|---|---|---|
| Connection Start | Plaintext until STARTTLS | Encrypted immediately |
| Downgrade Risk | Possible (STARTTLS stripped) | None (TLS required) |
| RFC Status | RFC 3207, 6409 | RFC 8314 (re-standardized 2018) |
| Firewall Visibility | Initial EHLO visible | Only TLS handshake visible |
| Client Support | Universal | Nearly universal (modern clients) |
| Recommended | Yes (with mandatory enforcement) | Yes (preferred for submission) |
STARTTLS Risks:
While widely deployed, STARTTLS has a fundamental vulnerability: the upgrade is negotiated over the plaintext connection. An active attacker can:
Mitigation:
1234567891011121314151617181920212223
# Port 587 - Submission with STARTTLSsubmission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_reject_unlisted_recipient=no -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_sender_restrictions=reject_sender_login_mismatch -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING # Port 465 - Submission with Implicit TLS (smtps)smtps inet n - y - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_sender_restrictions=reject_sender_login_mismatch -o smtpd_recipient_restrictions=permit_sasl_authenticated,rejectRFC 8314 (2018) recommends implicit TLS on port 465 for email submission over STARTTLS on port 587. Implicit TLS eliminates downgrade attack possibilities and reduces complexity. Configure clients to prefer port 465 when available.
Submission servers enforce limits to prevent abuse, manage resources, and ensure fair usage. Understanding these limits is crucial for applications that send email programmatically.
Size Limits:
Servers advertise maximum message size via the SIZE extension:
250-SIZE 52428800 (50 MB limit)
Clients should declare message size upfront:
MAIL FROM:<alice@example.org> SIZE=1048576
If the declared size exceeds the limit, the server rejects immediately:
552 5.3.4 Message size exceeds fixed maximum message size
| Limit Type | Typical Value | Purpose | Enforcement Point |
|---|---|---|---|
| Message Size | 10-50 MB | Prevent oversized messages | MAIL FROM with SIZE |
| Recipients per Message | 100-500 | Prevent mass mailing | RCPT TO count |
| Messages per Hour | 100-1000 | Rate limiting | Post-AUTH tracking |
| Messages per Day | 500-2000 | Daily quota | Cumulative tracking |
| Connection Rate | 10-50/hour | Prevent abuse | Pre-AUTH by IP |
| Concurrent Connections | 5-20 | Resource protection | Per-IP/per-user |
Rate Limiting Strategies:
Per-User Limits: After authentication, the server tracks how many messages each user sends. This allows legitimate high-volume users to have higher limits while restricting compromised accounts.
# Example: User alice exceeds 100 messages/hour
450 4.7.1 User alice sending limit exceeded. Try again later.
Per-IP Limits: Before authentication, limits apply per source IP. This protects against credential stuffing and brute-force attacks.
# Example: IP 192.168.1.50 made too many connection attempts
421 4.7.0 Too many connections from your IP. Try again later.
Exponential Backoff: Well-designed servers implement progressive delays for repeated failures:
1st failure: 5-second delay 2nd failure: 10-second delay 3rd failure: 30-second delay ...
When building applications that send email, implement client-side rate limiting to stay well below server limits. Use exponential backoff when receiving 4xx responses. Queue messages locally and send in batches to avoid hitting per-connection limits.
Proper mail submission configuration is critical for deliverability, security, and user experience. These best practices apply to both server administrators and application developers.
smtpd_tls_security_level=encrypt or use implicit TLS (port 465)reject_sender_login_mismatch to prevent spoofing123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
import smtplibimport sslfrom email.message import EmailMessagefrom email.utils import make_msgid, formatdate def send_email( smtp_server: str, port: int, username: str, password: str, from_addr: str, to_addr: str, subject: str, body: str) -> bool: """ Send email using best practices for mail submission. """ # Create message with all required headers msg = EmailMessage() msg['From'] = from_addr msg['To'] = to_addr msg['Subject'] = subject msg['Date'] = formatdate(localtime=True) msg['Message-ID'] = make_msgid(domain=from_addr.split('@')[1]) msg.set_content(body) # Create secure SSL context context = ssl.create_default_context() try: if port == 465: # Implicit TLS - preferred with smtplib.SMTP_SSL(smtp_server, port, context=context) as server: server.login(username, password) server.send_message(msg) else: # STARTTLS (port 587) with smtplib.SMTP(smtp_server, port) as server: server.starttls(context=context) # Upgrade to TLS server.login(username, password) server.send_message(msg) return True except smtplib.SMTPAuthenticationError as e: print(f"Authentication failed: {e}") return False except smtplib.SMTPException as e: print(f"SMTP error: {e}") return False # Example usagesend_email( smtp_server="mail.example.com", port=465, # Prefer implicit TLS username="alice@example.com", password="app_password_here", # Use app password or OAuth from_addr="alice@example.com", to_addr="bob@example.org", subject="Test from Python", body="This is a properly submitted email.")The example above shows credentials in code for clarity. In production, always load credentials from environment variables, secrets management services (AWS Secrets Manager, HashiCorp Vault), or secure configuration files with proper permissions.
Mail submission failures can stem from authentication issues, TLS problems, policy rejections, or network configuration. Understanding common errors accelerates troubleshooting.
| Error Code | Meaning | Common Causes | Solution |
|---|---|---|---|
| 421 4.7.0 | Connection rejected | IP reputation, rate limiting, server maintenance | Wait and retry; check IP blacklists |
| 450 4.7.1 | Temporary auth failure | Account locked, rate limit exceeded | Wait for timeout; check account status |
| 454 4.7.0 | TLS not available | Server config issue, STARTTLS failure | Check server TLS config; try port 465 |
| 503 5.5.1 | AUTH not permitted | Sent AUTH before EHLO or after MAIL | Follow correct command sequence |
| 530 5.7.0 | Auth required | Tried to send without authenticating | Add AUTH before MAIL FROM |
| 535 5.7.8 | Auth failed | Wrong username/password, expired token | Verify credentials; renew OAuth token |
| 550 5.7.1 | Sender not allowed | Trying to send from unauthorized address | Use address associated with auth account |
| 552 5.2.3 | Message too large | Exceeds server SIZE limit | Reduce attachments; use file sharing |
| 553 5.1.3 | Invalid sender | Malformed sender address syntax | Fix email address format |
Troubleshooting Workflow:
1. Test Connectivity:
# Test if port is reachable
nc -zv mail.example.com 587
telnet mail.example.com 587
2. Test TLS:
# Test STARTTLS
openssl s_client -starttls smtp -connect mail.example.com:587
# Test implicit TLS
openssl s_client -connect mail.example.com:465
3. Check Server Capabilities:
EHLO test.local
# Examine response for AUTH, STARTTLS, SIZE
4. Verify Authentication:
AUTH PLAIN <base64_credentials>
# Check for 235 success or 535 failure
5. Review Logs: Server-side logs reveal detailed rejection reasons not always shown in SMTP responses.
Use swaks (Swiss Army Knife for SMTP) for comprehensive testing. It handles TLS, authentication, and message composition in a single command: swaks --to recipient@example.com --from sender@example.org --server mail.example.com:587 --tls --auth-user username --auth-password password
We've thoroughly explored mail submission—the critical first step where email clients authenticate with and hand off messages to mail servers. Let's consolidate the essential points:
What's Next:
With mail submission mastered, we turn to Mail Relay—how servers route email across the Internet, from the sending organization to the recipient's domain. We'll explore MX record lookups, relay authorization, multi-hop delivery, and the complex trust relationships that enable global email routing.
You now understand mail submission comprehensively—from the MSA's role and authentication mechanisms to TLS requirements and implementation best practices. This knowledge enables you to configure mail clients, build email-sending applications, troubleshoot submission failures, and secure submission infrastructure.