The Problem#
A few days ago I tried to automate sending emails from my home server. Nothing complicated: a Python script with smtplib to send notifications. The problem came when I configured a password with special characters: MiPasw0rd$Ñ.
import smtplib
server = smtplib.SMTP('mail.example.com', 587)
server.starttls()
server.login('usuario@example.com', 'MiPasw0rd$Ñ') # Error 535Result: SMTPAuthenticationError: (535, b'5.7.8 Authentication credentials invalid')
The strange thing is that the password was correct. I could access it manually without problems. Error 535 suggested authentication failures, but the real problem was in the encoding.
Why It Fails#
The fault lies in smtplib’s encoding handling. The login() method uses UTF-8 by default, but then applies transformations that don’t always respect special characters correctly, especially when the server or library have legacy configurations.
In my case, the SMTP server expected consistent UTF-8 encoding. When smtplib processed the password with $ and Ñ, something along the way got corrupted.
The Solution: Manual AUTH LOGIN#
The AUTH LOGIN protocol is simple: username and password are encoded in base64 separately and sent in manual steps. This gives you complete control over the encoding.
import smtplib
import base64
def auth_login_manual(server, usuario, contraseña):
"""Implementa AUTH LOGIN manualmente con UTF-8"""
# Respuesta inicial del servidor
code, response = server.docmd("AUTH LOGIN")
# Codificar usuario en base64 UTF-8
usuario_b64 = base64.b64encode(usuario.encode('utf-8')).decode('ascii')
code, response = server.docmd(usuario_b64)
# Codificar contraseña en base64 UTF-8
contraseña_b64 = base64.b64encode(contraseña.encode('utf-8')).decode('ascii')
code, response = server.docmd(contraseña_b64)
# Verificar éxito (código 235 es autenticación exitosa)
if code != 235:
raise Exception(f"Autenticación fallida: {code} {response}")
return True
# Uso
server = smtplib.SMTP('mail.example.com', 587)
server.starttls()
auth_login_manual(server, 'usuario@example.com', 'MiPasw0rd$Ñ')
print("Autenticado exitosamente")Protocol Breakdown#
- AUTH LOGIN: The client requests to use the AUTH LOGIN mechanism
- Username in base64: The server responds with 334, expects the encoded username
- Password in base64: The server responds with 334, expects the encoded password
- Response 235: Indicates successful authentication
The docmd() method sends raw SMTP commands and returns the response code and message.
Testing on My Server#
I implemented this in my home infrastructure with Postfix. The difference is noticeable:
# Antes: falla con caracteres especiales
server.login('admin@local.home', 'P@ssw0rdÑ') # Error 535
# Después: funciona perfectamente
auth_login_manual(server, 'admin@local.home', 'P@ssw0rdÑ') # OKSecurity Considerations#
- Base64 is not encryption: always use STARTTLS or direct connection to port 465 with SSL
- UTF-8 encoding is safe for special characters
- This method is compatible with any SMTP server that supports AUTH LOGIN
Complete Script#
import smtplib
import base64
class SMTPConnection:
def __init__(self, host, port=587):
self.server = smtplib.SMTP(host, port)
self.server.starttls()
def login(self, usuario, contraseña):
self.server.docmd("AUTH LOGIN")
self.server.docmd(base64.b64encode(usuario.encode('utf-8')).decode('ascii'))
code, resp = self.server.docmd(base64.b64encode(contraseña.encode('utf-8')).decode('ascii'))
if code != 235:
raise Exception(f"Auth failed: {code}")
def send(self, de, para, asunto, cuerpo):
msg = f"From: {de}\nTo: {para}\nSubject: {asunto}\n\n{cuerpo}"
self.server.sendmail(de, para, msg)
def close(self):
self.server.quit()
# Uso
smtp = SMTPConnection('mail.example.com')
smtp.login('usuario@example.com', 'MiPasw0rd$Ñ')
smtp.send('usuario@example.com', 'destino@example.com', 'Test', 'Funciona!')
smtp.close()Conclusion#
If your SMTP server rejects passwords with special characters, it’s not a mystery. It’s an encoding problem. Implementing AUTH LOGIN manually gives you complete control and works with any password.
I deployed it in production on my home server and haven’t had any problems since.
Recommended Equipment#
- Raspberry Pi 3 B+ — Lightweight, low-consumption server to start your homelab
- Raspberry Pi 4 (4GB) — The perfect foundation for homelab, Docker, and monitoring
Affiliate links. No extra cost to you.