guía · SMTP con Python
¿Cómo debe implementar un equipo de producto SMTP con Python de forma segura?
Implemente SMTP en Python desde un worker en segundo plano autorizado, no directamente desde una solicitud web. Construya el mensaje con EmailMessage, use SMTP_SSL para TLS implícito o actualice explícitamente con STARTTLS cuando lo exija el contrato vigente del proveedor, autentíquese con un secreto del lado del servidor y llame a send_message con tiempos de espera acotados. Guarde la tarea antes de conectarse, registre la evidencia de rechazo por destinatario, concilie las desconexiones ambiguas y distinga la aceptación SMTP de la entrega posterior y de la llegada a la bandeja de entrada.
Autorice y guarde el envío antes de usar SMTP
Empiece con un evento legítimo de la aplicación, como un recibo, una alerta de seguridad, una verificación solicitada o un aviso de la cuenta. Autentique al llamante y autorice el inquilino, la clase de mensaje, la identidad From visible, el destinatario y la revisión de la plantilla. Escriba una tarea saliente duradera con una clave estable del evento de negocio antes de abrir cualquier conexión SMTP. Esa clave debe impedir que dos workers creen de forma independiente el mismo mensaje lógico. Los datos del navegador no deben elegir el host SMTP, el puerto, el nombre de usuario, el remitente del sobre, destinatarios arbitrarios, encabezados ni la política de TLS. Mantenga esos valores en una configuración de servidor revisada. Un worker de cola debe reclamar una tarea, volver a comprobar las supresiones y la autorización en el momento del envío, registrar cada intento y liberar o finalizar la tarea mediante estados explícitos. La biblioteca SMTP de Python transporta el mensaje preparado; no aporta autorización por inquilino, consentimiento, idempotencia ni política de supresión.
Construya el mensaje con EmailMessage
Use email.message.EmailMessage en lugar de concatenar encabezados y cuerpos sin procesar. Establezca From, To, Subject, Date y un Message-ID generado según el modelo aprobado de la aplicación, y luego use set_content para el texto y add_alternative para el HTML cuando sea necesario. Valide los objetos de dirección, limite el número de destinatarios y adjuntos, rechace la inyección de saltos de línea en los valores y escape los datos de la plantilla según el contexto de salida. Genere el texto y el HTML a partir de una única revisión inmutable de la plantilla. Mantenga los secretos y los datos personales innecesarios fuera de los asuntos, los encabezados personalizados, los nombres de archivo, los campos de diagnóstico y los registros. Separe deliberadamente el encabezado From visible del remitente del sobre SMTP, porque la autenticación y el procesamiento de rebotes pueden depender de identidades distintas. Guarde una revisión del contenido o un hash que respete la privacidad para la auditoría, en lugar de conservar los cuerpos completos de los mensajes sin una necesidad definida.
Elija de forma explícita TLS implícito o STARTTLS
Python documenta SMTP_SSL para las conexiones cifradas desde el inicio y SMTP.starttls para actualizar una conexión ya establecida. Siga el nombre de host, el puerto, el certificado y el contrato de envío vigentes del proveedor en lugar de adivinarlos a partir de una lista genérica de puertos. Cree un contexto SSL predeterminado verificado y no desactive las comprobaciones del certificado ni del nombre de host. Para STARTTLS, conéctese, emita EHLO según sea necesario, llame a starttls con el contexto y vuelva a emitir EHLO, porque las extensiones anunciadas pueden cambiar tras la actualización. Nunca envíe credenciales ni contenido de mensajes de clientes por una conexión sin cifrar. RFC 8314 recomienda el envío protegido con TLS y declara obsoleto el acceso en texto sin cifrar. Trate un fallo de certificado, una discrepancia del nombre de host, la ausencia de un STARTTLS obligatorio o cambios inesperados de capacidades como fallos graves que requieren investigación, en lugar de recurrir en silencio a una alternativa.
Mantenga las credenciales SMTP dentro de un límite de secretos restringido
Cargue el nombre de usuario y la contraseña o el token desde un servicio de secretos gestionado del lado del servidor en tiempo de ejecución. No coloque credenciales en el código fuente, en paquetes de cliente, en volcados del entorno, en URL, en trazas de excepciones, en analíticas, en notebooks, en capturas de pantalla, en prompts ni en fixtures confirmados en el repositorio. Limite cada credencial al entorno y a la carga de trabajo más pequeños que admita el proveedor, y separe el desarrollo de la producción. Autentíquese solo después de establecer el estado de TLS requerido. Practique la rotación con destinatarios controlados: aprovisione el reemplazo mediante la administración aprobada, actualice el worker, confirme la autenticación y un ciclo de vida completo de eventos, y luego revoque el valor anterior. Los fallos de autenticación repetidos deben pausar la ruta afectada en lugar de desencadenar un bucle de reintentos rápidos. El método login de Python negocia entre los mecanismos que anuncia el servidor, pero el mecanismo real del proveedor, la política de la cuenta, los permisos del token y el comportamiento de rotación requieren evidencia actual.
Use una función de envío en Python acotada
Mantenga pequeño el adaptador del proveedor y devuelva evidencia estructurada a la máquina de estados de la tarea. Un flujo típico crea un contexto SSL, abre SMTP_SSL(host, port, timeout=10) as smtp para TLS implícito, llama a smtp.login(username, secret) y después llama a smtp.send_message(message, from_addr=envelope_from, to_addrs=recipients). Para un proveedor que exija una actualización explícita, use SMTP con un tiempo de espera, ehlo, starttls(context=context), ehlo y luego login. No presente los nombres de host ni los puertos de ejemplo como valores predeterminados universales. Pase una lista de destinatarios normalizada en lugar de depender del análisis de encabezados que no son de confianza. Capture la clase de excepción, el código de respuesta SMTP y un texto de diagnóstico acotado cuando estén disponibles, pero oculte las direcciones, las credenciales y el contenido del mensaje. Mida por separado las fases de conexión, TLS, autenticación, sobre, datos y cierre (quit) para que los fallos operativos sigan siendo diagnosticables.
Interprete con precisión los resultados por destinatario de send_message
Python documenta que sendmail y send_message terminan normalmente cuando el correo se acepta para al menos un destinatario y devuelven un diccionario con los destinatarios rechazados; un diccionario vacío significa que no se rechazó a ningún destinatario en esa etapa. Conserve ese resultado por destinatario en lugar de marcar toda la tarea como entregada. Si se rechaza a todos los destinatarios, la biblioteca lanza una excepción SMTPRecipientsRefused. Otras excepciones distinguen el rechazo del remitente, el rechazo de DATA y los errores de autenticación, de conexión, de protocolo y relacionados. Asigne la evidencia exacta a estados de la aplicación: aceptado por el servidor de envío, rechazado de forma permanente, rechazado de forma transitoria o desconocido. Un retorno normal solo demuestra el resultado acotado del envío SMTP. No establece la aceptación por el servidor de destino, la ubicación final en el buzón, la lectura ni la interacción. Las notificaciones de estado de entrega o los eventos del proveedor posteriores deben correlacionarse por separado.
Reintente solo cuando el riesgo de duplicados esté controlado
Clasifique los fallos antes de programar otro intento. Los fallos permanentes de dirección, remitente, autenticación, política o contenido suelen requerir una corrección o una supresión, no una repetición automática. Las respuestas 4xx transitorias pueden reintentarse con backoff exponencial, jitter, un máximo de intentos, una caducidad y un presupuesto por destino. Un reinicio de la conexión o un tiempo de espera agotado después de enviar los datos del mensaje puede ser ambiguo: el servidor podría haber aceptado el mensaje mientras el cliente no recibió su respuesta final. Mantenga ese intento como desconocido, inspeccione la actividad del proveedor o los eventos posteriores mediante una correlación que respete la privacidad, y evite reenviar a ciegas de inmediato. SMTP no tiene una clave de idempotencia de aplicación universal. La clave duradera del evento de negocio evita intentos simultáneos de la aplicación, pero no puede obligar a un servidor SMTP remoto a deduplicar dos envíos aceptados. Escale los resultados ambiguos repetidos y conserve la evidencia exacta en la que se basó la decisión.
Gestione los destinatarios parciales y las supresiones
Cuando un mensaje tiene varios destinatarios, SMTP puede aceptar algunos y rechazar otros. Guarde la respuesta de cada destinatario y haga avanzar al siguiente estado solo el subconjunto aceptado. No reenvíe toda la lista original solo porque una dirección recibió un rechazo transitorio. Aplique las supresiones por rebote permanente, queja, baja, motivos legales, inquilino y administrador antes de cada intento, incluidos los reintentos. Separe las clases de mensaje solo mediante una política explícita y documentada; etiquetar un mensaje como transaccional no elimina la seguridad del destinatario ni las restricciones del proveedor. Prefiera tareas con un solo destinatario en los flujos sensibles cuando la privacidad y el estado individualizado justifiquen el costo. Evite exponer las listas de destinatarios mediante To o Cc, y nunca use el comportamiento de Bcc como sustituto de la autorización. Limite y oculte el texto de diagnóstico, porque las respuestas SMTP pueden contener direcciones de destinatarios o detalles específicos del receptor.
Pruebe las rutas de fallo con sistemas controlados
Pruebe la construcción de mensajes, Unicode, las alternativas en texto y HTML, los adjuntos, el rechazo de encabezados, la normalización de destinatarios, la verificación de TLS, la ausencia de STARTTLS, las credenciales no válidas, el rechazo del remitente, el rechazo de uno y de todos los destinatarios, el rechazo de DATA, los tiempos de espera antes y después de una posible aceptación, las desconexiones, las respuestas de límite de frecuencia, la caducidad de los reintentos, los workers duplicados, los cambios de supresión y la rotación de secretos. Use un servicio SMTP de prueba controlado o una simulación local para las pruebas unitarias y de integración deterministas; nunca enrute por accidente tráfico de entornos inferiores a direcciones de clientes. En los canarios de producción, use destinatarios autorizados e inspeccione los encabezados sin procesar para comprobar el From visible, la ruta del sobre, el Message-ID, DKIM, SPF, la alineación DMARC y la evidencia del proveedor. Confirme que los registros y las métricas no filtran credenciales ni cuerpos de mensajes. No lance si el worker puede eludir la autorización por inquilino, degradar TLS, reintentar sin límites, ignorar rechazos parciales o no puede pausar la ruta de envío.
Cómo encaja SendHQ
SendHQ es una API de correo electrónico con alcance por espacio de trabajo para comunicaciones de producto esperadas. Su documentación cubre el envío, los dominios verificados, los eventos de entrega y las supresiones. Use su API HTTP documentada al integrar SendHQ con Python.
Preguntas frecuentes
¿Python debe usar SMTP_SSL o STARTTLS?
Use el modo que exija el contrato de envío vigente del proveedor. SMTP_SSL cifra desde el inicio de la conexión; STARTTLS actualiza la conexión de forma explícita y requiere TLS verificado y un nuevo EHLO.
¿Que send_message termine normalmente demuestra la entrega?
No. Significa que se aceptó al menos un destinatario en esa etapa del envío SMTP. La aceptación por el destino, la ubicación en el buzón y la interacción requieren evidencia posterior y acotada.
¿Qué significa el diccionario que devuelve send_message?
Asocia los destinatarios rechazados por el servidor SMTP con la evidencia de la respuesta. Un diccionario vacío significa que no se rechazó a ninguno en esa etapa, no que todos los mensajes llegaran a una bandeja de entrada.
¿Se puede reintentar de inmediato tras un tiempo de espera agotado?
No de forma segura si ocurrió después de un posible envío. Conserve el intento como ambiguo, concilie la evidencia del proveedor o de eventos posteriores y reenvíe solo con una política acotada del riesgo de duplicados.
¿Dónde se debe guardar la contraseña SMTP?
Use un servicio de secretos gestionado del lado del servidor con acceso restringido por carga de trabajo y entorno, recuperación auditada, rotación probada y sin exposición a clientes, registros, prompts ni fixtures.
¿Se debe desactivar alguna vez la verificación de certificados en producción?
No. Un fallo de certificado o de nombre de host es evidencia de una configuración insegura o incorrecta. Detenga la ruta y diagnostíquela en lugar de debilitar en silencio la verificación de TLS.
¿Cómo se debe gestionar el rechazo parcial de destinatarios?
Guarde el resultado de cada destinatario, haga avanzar el subconjunto aceptado y reintente solo los rechazos transitorios que lo admitan. No reenvíe a los destinatarios ya aceptados junto con toda la lista original.
¿Demuestra esta página que SendHQ admite SMTP?
No. Esta guía cubre SMTP de Python en general; use la documentación actual de SendHQ para su API de correo electrónico.
Fuentes
- smtplib: cliente del protocolo SMTP — Python Software Foundation
- email.message: representación de un mensaje de correo electrónico — Python Software Foundation
- Ejemplos de correo electrónico en Python — Python Software Foundation
- RFC 5321: protocolo simple de transferencia de correo (SMTP) — RFC Editor
- RFC 4954: extensión del servicio SMTP para la autenticación — RFC Editor
- RFC 8314: El texto sin cifrar se considera obsoleto: uso de Transport Layer Security (TLS) para envío y acceso de correo — RFC Editor