guia · SMTP com Python

Como uma equipe de produto deve implementar SMTP com Python de forma segura?

Implemente o SMTP em Python a partir de um worker em segundo plano autorizado, não diretamente de uma requisição web. Monte a mensagem com EmailMessage, use SMTP_SSL para TLS implícito ou faça o upgrade explícito com STARTTLS quando o contrato atual do provedor exigir, autentique com um segredo guardado no servidor e chame send_message com timeouts limitados. Persista o job antes de se conectar, registre as evidências de recusa por destinatário, reconcilie desconexões ambíguas e diferencie a aceitação SMTP da entrega posterior e da chegada à caixa de entrada.

Autorize e persista o envio antes do SMTP

Comece com um evento legítimo da aplicação, como um recibo, alerta de segurança, verificação solicitada ou aviso da conta. Autentique o chamador e autorize o tenant, a classe de mensagem, a identidade From visível, o destinatário e a revisão do template. Grave um job de saída durável com uma chave estável do evento de negócio antes de abrir qualquer conexão SMTP. Essa chave deve impedir que dois workers criem de forma independente a mesma mensagem lógica. A entrada do navegador não pode escolher o host SMTP, a porta, o nome de usuário, o remetente do envelope, destinatários arbitrários, cabeçalhos ou a política de TLS. Mantenha esses valores em uma configuração de servidor revisada. Um worker de fila deve reivindicar um job, verificar novamente supressões e autorização no momento do envio, registrar cada tentativa e liberar ou finalizar o job por meio de estados explícitos. A biblioteca SMTP do Python transporta a mensagem preparada; ela não fornece autorização de tenant, consentimento, idempotência nem política de supressão.

Monte a mensagem com EmailMessage

Use email.message.EmailMessage em vez de concatenar cabeçalhos e corpos brutos. Defina From, To, Subject, Date e um Message-ID gerado de acordo com o modelo aprovado da aplicação e depois use set_content para o texto e add_alternative para o HTML, quando necessário. Valide os objetos de endereço, limite a quantidade de destinatários e anexos, rejeite injeção de quebras de linha nos valores e escape os dados do template para o contexto de saída. Gere o texto e o HTML a partir de uma única revisão imutável do template. Mantenha segredos e dados pessoais desnecessários fora de assuntos, cabeçalhos personalizados, nomes de arquivos, campos de diagnóstico e logs. Separe deliberadamente o cabeçalho From visível do remetente do envelope SMTP, porque a autenticação e o processamento de bounces podem depender de identidades diferentes. Armazene uma revisão do conteúdo ou um hash que preserve a privacidade para auditoria, em vez de guardar os corpos completos das mensagens sem uma necessidade definida.

Escolha explicitamente entre TLS implícito e STARTTLS

O Python documenta o SMTP_SSL para conexões criptografadas desde o início e o SMTP.starttls para fazer o upgrade de uma conexão já estabelecida. Siga o hostname, a porta, o certificado e o contrato de submissão atuais do provedor, em vez de adivinhar a partir de uma lista genérica de portas. Crie um contexto SSL padrão verificado e não desative as verificações de certificado ou de hostname. Para STARTTLS, conecte-se, envie EHLO conforme necessário, chame starttls com o contexto e envie EHLO novamente, porque as extensões anunciadas podem mudar após o upgrade. Nunca envie credenciais ou conteúdo de mensagens de clientes por uma conexão em texto puro. A RFC 8314 recomenda a submissão protegida por TLS e desaconselha o acesso em texto puro. Trate falha de certificado, hostname divergente, STARTTLS obrigatório ausente ou mudanças inesperadas de capacidades como falhas graves que exigem investigação, em vez de fazer um fallback silencioso.

Mantenha as credenciais SMTP em uma fronteira de segredos restrita

Carregue o nome de usuário e a senha ou o token de um serviço gerenciado de segredos no servidor, em tempo de execução. Não coloque credenciais em código-fonte, bundles do cliente, dumps de ambiente, URLs, traces de exceção, analytics, notebooks, capturas de tela, prompts ou fixtures versionadas. Restrinja cada credencial ao menor ambiente e à menor carga de trabalho que o provedor suportar e separe desenvolvimento de produção. Autentique somente depois que o estado de TLS exigido estiver estabelecido. Exercite a rotação com destinatários controlados: provisione a substituta pela administração aprovada, atualize o worker, confirme a autenticação e um ciclo de vida completo de eventos e depois revogue o valor antigo. Falhas de autenticação repetidas devem pausar a rota afetada, em vez de disparar um loop rápido de novas tentativas. O método login do Python negocia entre os mecanismos anunciados pelo servidor, mas o mecanismo real do provedor, a política da conta, as permissões do token e o comportamento de rotação exigem evidências atuais.

Use uma função de envio em Python com limites

Mantenha o adaptador do provedor pequeno e retorne evidências estruturadas para a máquina de estados do job. Um fluxo típico cria um contexto SSL, abre SMTP_SSL(host, port, timeout=10) as smtp para TLS implícito, chama smtp.login(username, secret) e depois chama smtp.send_message(message, from_addr=envelope_from, to_addrs=recipients). Para um provedor que exige upgrade explícito, use SMTP com timeout, ehlo, starttls(context=context), ehlo e então login. Não apresente hostnames ou portas de exemplo como padrões universais. Passe uma lista normalizada de destinatários, em vez de depender do parsing de cabeçalhos não confiáveis. Capture a classe da exceção, o código de resposta SMTP e um texto de diagnóstico limitado quando disponíveis, mas oculte endereços, credenciais e conteúdo das mensagens. Meça separadamente as fases de conexão, TLS, autenticação, envelope, dados e encerramento, para que as falhas operacionais continuem diagnosticáveis.

Interprete com precisão os resultados por destinatário do send_message

O Python documenta que sendmail e send_message retornam normalmente quando o e-mail é aceito para pelo menos um destinatário e retornam um dicionário com os destinatários recusados; um dicionário vazio significa que nenhum destinatário foi recusado naquela etapa. Preserve esse resultado por destinatário, em vez de marcar o job inteiro como entregue. Se todos os destinatários forem recusados, a biblioteca lança uma exceção SMTPRecipientsRefused. Outras exceções diferenciam recusa do remetente, recusa do DATA, autenticação, conexão, protocolo e erros relacionados. Mapeie as evidências exatas para estados da aplicação: aceito pelo servidor de submissão, recusado permanentemente, recusado temporariamente ou desconhecido. Um retorno normal comprova apenas o resultado da submissão SMTP naquele escopo. Ele não estabelece a aceitação pelo servidor de destino, o posicionamento final na caixa postal, a leitura nem o engajamento. Notificações de status de entrega posteriores ou eventos do provedor precisam ser correlacionados separadamente.

Tente novamente somente quando o risco de duplicatas estiver controlado

Classifique as falhas antes de agendar outra tentativa. Falhas permanentes de endereço, remetente, autenticação, política ou conteúdo geralmente exigem correção ou supressão, não repetição automatizada. Respostas 4xx transitórias podem ser tentadas novamente com backoff exponencial, jitter, teto de tentativas, expiração e um orçamento por destino. Um reset de conexão ou timeout depois do envio dos dados da mensagem pode ser ambíguo: o servidor pode ter aceitado a mensagem enquanto o cliente perdeu a resposta final. Mantenha essa tentativa como desconhecida, inspecione a atividade do provedor ou eventos posteriores por meio de uma correlação que preserve a privacidade e evite reenviar às cegas imediatamente. O SMTP não tem uma chave de idempotência universal da aplicação. A chave durável do evento de negócio impede tentativas concorrentes da aplicação, mas não consegue forçar um servidor SMTP remoto a eliminar duplicatas de duas submissões aceitas. Escale resultados ambíguos repetidos e preserve as evidências exatas usadas na decisão.

Trate destinatários parciais e supressões

Quando uma mensagem tem vários destinatários, o SMTP pode aceitar alguns e recusar outros. Armazene a resposta de cada destinatário e avance para o próximo estado apenas o subconjunto aceito. Não reenvie a lista original inteira só porque um endereço recebeu uma recusa transitória. Aplique supressões por bounce permanente, reclamação, descadastro, questões legais, tenant e administrador antes de cada tentativa, inclusive nas novas tentativas. Separe classes de mensagem apenas por uma política explícita e documentada; rotular uma mensagem como transacional não apaga a segurança dos destinatários nem as restrições do provedor. Prefira jobs com um único destinatário em fluxos sensíveis, quando a privacidade e o estado individualizado justificarem o custo. Evite expor listas de destinatários em To ou Cc e nunca use o comportamento do Bcc como substituto da autorização. Limite e oculte o texto de diagnóstico, porque as respostas SMTP podem conter endereços de destinatários ou detalhes específicos do receptor.

Teste os caminhos de falha com sistemas controlados

Teste a montagem de mensagens, Unicode, alternativas em texto e HTML, anexos, rejeição de cabeçalhos, normalização de destinatários, verificação de TLS, STARTTLS ausente, credenciais inválidas, recusa do remetente, recusa de um e de todos os destinatários, recusa do DATA, timeouts antes e depois de uma possível aceitação, desconexões, respostas de limite de taxa, expiração das novas tentativas, workers duplicados, mudanças de supressão e rotação de segredos. Use um serviço SMTP de teste controlado ou um fake local para testes unitários e de integração determinísticos; nunca roteie tráfego acidental de ambientes inferiores para endereços de clientes. Em canários de produção, use destinatários autorizados e inspecione os cabeçalhos brutos para conferir o From visível, o caminho do envelope, o Message-ID, o DKIM, o SPF, o alinhamento DMARC e as evidências do provedor. Confirme que logs e métricas não vazam credenciais nem corpos de mensagens. Bloqueie o lançamento se o worker conseguir contornar a autorização do tenant, rebaixar o TLS, tentar novamente sem limites, ignorar recusas parciais ou não conseguir pausar a rota de envio.

Como o SendHQ se encaixa

O SendHQ é uma API de e-mail com escopo por workspace para comunicação de produto esperada. Sua documentação abrange envio, domínios verificados, eventos de entrega e supressões. Use a API HTTP documentada ao integrar o SendHQ ao Python.

Perguntas frequentes

O Python deve usar SMTP_SSL ou STARTTLS?

Use o modo exigido pelo contrato de submissão atual do provedor. O SMTP_SSL criptografa desde o início da conexão; o STARTTLS faz o upgrade explicitamente e exige TLS verificado e um novo EHLO.

Se o send_message retornar normalmente, isso comprova a entrega?

Não. Significa que pelo menos um destinatário foi aceito naquela etapa de submissão SMTP. A aceitação no destino, o posicionamento na caixa postal e o engajamento exigem evidências posteriores com escopo definido.

O que significa o dicionário retornado pelo send_message?

Ele associa os destinatários recusados pelo servidor SMTP às evidências de resposta. Um dicionário vazio significa que nenhum foi recusado naquela etapa, não que todas as mensagens chegaram a uma caixa de entrada.

Um timeout pode ser tentado novamente de imediato?

Não com segurança quando ocorreu depois de uma possível submissão. Preserve a tentativa como ambígua, reconcilie as evidências do provedor ou de eventos posteriores e reenvie somente sob uma política limitada de risco de duplicatas.

Onde a senha SMTP deve ser armazenada?

Use um serviço gerenciado de segredos no servidor, com acesso restrito por carga de trabalho e ambiente, recuperação auditada, rotação testada e nenhuma exposição a clientes, logs, prompts ou fixtures.

A verificação de certificados deve ser desativada em produção em algum caso?

Não. Uma falha de certificado ou de hostname é evidência de uma configuração insegura ou incorreta. Interrompa a rota e diagnostique o problema, em vez de enfraquecer silenciosamente a verificação de TLS.

Como tratar a recusa parcial de destinatários?

Persista o resultado de cada destinatário, avance o subconjunto aceito e tente novamente apenas as recusas transitórias elegíveis. Não reenvie para destinatários já aceitos junto com a lista original inteira.

Esta página comprova que o SendHQ suporta SMTP?

Não. Este guia aborda SMTP no Python em geral; use a documentação atual do SendHQ para sua API de e-mail.

Fontes