DKIM fails when sending mails with smtplib - python

I'm trying to send emails with smtplib and they seem to be delivering fine. The only problem is that DKIM fails and the mails usually go straight to the spam folder.
DKIM is enabled on my shared hosting (host is a2hosting, if that helps) and the process works fine when sending individual emails with Thunderbird, and DKIM passes, suggesting that the problem lies on my end.
I even tried using dkimpy to explicitly sign the emails using the private key but I still get dkim=fail under ARC-Authentication-Results.
Some posts and answers I referred to suggested "logging in" as the solution but I am already logging in using SMTP.login() and as I mentioned earlier, the emails are being sent.
An answer I referred to mentioned that it is the server's job to sign the email and it's worth mentioning that the raw email output includes the DKIM signature, even without explicitly signing it with dkimpy, indicating that the server is signing as expected.
But the problem remains that DKIM fails affecting the email deliverability, and the raw output does not provide any details as to why DKIM failed for the domain.
I use the following code snippet to send an email
msg = MIMEMultipart()
msg['From'] = 'myemail#mydomain.tld'
msg['To'] = 'someemail#gmail.com'
msg['Subject'] = "Subject"
msg.attach(MIMEText("SomeText", "plain"))
s = smtplib.SMTP_SSL("mydomain.tld:465")
s.login("myemail#mydomain.tld", "mypassword")
s.sendmail("myemail#mydomain.tld", 'someemail#gmail.com',msg.as_string())
I tried signing the message as follows
headers = ["To", "From", "Subject"]
with open("cert.pem") as fh:
dkim_private = fh.read()
sig = dkim.sign(
message=msg.as_string().encode("ascii"),
selector=str(dkim_selector).encode("ascii"),
domain="robogyan.tech".encode("ascii"),
privkey=dkim_private.encode("ascii"),
include_headers=headers,)
msg["DKIM-Signature"] = sig.decode("ascii").lstrip("DKIM-Signature: ")
The raw output did reflect the signature with the above code but DKIM still failed.
There seems to be no problem with the authentication whatsoever since the server replies with "Authentication succeeded"
Edit:
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
d=mydomain.tld; s=default; h=Subject:To:From:MIME-Version:Content-Type:
Sender:Reply-To:Date:Message-ID:Cc:Content-Transfer-Encoding:Content-ID:
Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc
:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:
List-Subscribe:List-Post:List-Owner:List-Archive;
bh=giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=; b=DR08Q+CjgOLqo8WkLJs/XROfTw
Z7+ph+qnzi5p49cT3+UwQolcL1CKIVPk7XRkL8WZ3FFa9hZuc6TumquRSiYd5uR0AC5Z3lopEfnQe
fdbOOTRnks2ZzoOnQusy/gmydUttypu8wTthFhy7vTWXMFcdI29X/HkrokCtiGKCoD2u2kWBtn2sm
3/aP83lBbMpcWsNbvo3HTsL71o8QPd6bVKpqRGyAy89cAwMLwP4dnJ9WcCxxNzowlJNPQja3o5W16
t3rG/KizcRehjaDUXhPPRF/4RdYUSIi/SGNwmIPwvkZNc17k3wQpszKeG6/Ujgax/i7Li7V7dLJBT
Fu/x6xDA==;
Signed-by: myemail#mydomain.tld
Expected-Body-Hash: giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=
Here's the DKIM of the failing email if that helps. The expected body hash and the received body hash match too. I am not sure what the problem is then.

After a lot of research and brute force approaches, I finally found the solution to my problem.
I needed to include the Message-ID and the date in the headers as well.
Adding the following lines to the code helped me pass the verification.
msg['Date'] = email.utils.formatdate()
msg['Message-ID'] = email.utils.make_msgid(domain='mydomain.tld')

Important note: you need to add your smtp client's machine IP address to InternalHosts list, because OpenDKIM will check client's permission with these rules.
The you need to add this line to your /etc/opendkim.conf:
InternalHosts file:/etc/opendkim/TrustedHosts # or any location you want
Content of /etc/opendkim/TrustedHosts could look like:
127.0.0.1
::1
localhost
<server_ip>
hostname.example1.com
example1.com
hostname.example2.com
example2.com
...
It's just for example. You need to put here your python smtplib-client machine's address (ip/host).
Then just restart your opendkim:
$ sudo service opendkim restart

Related

I can't send emails with python

from mailer import Mailer
mail = Mailer(email='my email', password='my password')
mail.send(receiver='random email', subject="test", message="Cool reset")
When I run this I get an error, it says
Error: Email And Password Not Accepted.
Note:
Make sure you Allowed less secure apps,
if you didn't, visit this link:
==> https://myaccount.google.com/lesssecureapps
For More information visit this link:
==> https://support.google.com/mail/?p=BadCredentials
I've already set the "Allow less secure apps" to on.
Btw the email credentials aren't the actual ones I use in my code
The following works for me with my own credentials, you will just want to replace the curly braces with your data (EX: mail = Mailer("email", "password")):
from mailer import Mailer
mail = Mailer("{sender_email}", "{sender_password}")
mail.send(receiver="{receiver_email}", subject="{subject}", message={"message"})
I found this documentation for quick-mailer. Perhaps the issue is the use of single quotes, maybe try using only double quotes?
Take another stab at it with that info, good luck.

Get original e-mail sender server in Python

With many e-mail services, you can get tricked into believing an e-mail has been sent from a different address.
Using smtplib in Python, you can easily do the trick by manipulating the From argument.
gmail is not prone to that as they print the via argument which shows the original server.
However, what I cannot find anywhere is how do you retrieve the original (not manipulated) server name in Python, the same as gmail does with their via functionality?
I've tried the imaplib and email libraries, but there I can only access the already manipulated sender.
Any ideas? Is that solely linked to the configuration of a particular provider (e.g. Google, Outlook, hotmail, etc.), or can something be done regardless of that?
Here is part of the code I'm currently using (no success):
import imaplib
import email
obj = imaplib.IMAP4('imap', portn)
obj.login('username', 'password')
obj.select('INBOX')
uidl_list = [68720]
resp, data = obj.uid('FETCH', ','.join(map(str, uidl_list)) , '(BODY.PEEK[HEADER.FIELDS (From Subject)] RFC822.SIZE)')
Never heard about the via field. It is related to this particular provider.
You may check the Received headers of the mail to know what SMTP servers the message went through. Assuming those are not fake and were not modified along the way (i.e. assuming you trust the servers), they should point you to the SMTP server the user connected to to send the message.
Example:
Received: from mail-ot1-x333.google.com (mail-ot1-x333.google.com [IPv6:2617:f8c0:4864:20::331])
by smtp.domain.tld (Postfix) with ESMTPS id 6C488D0F8
for <user#domain.tld>; Mon, 19 Nov 2018 21:13:54 +0100 (CET)
Received: by mail-ot1-x333.google.com with SMTP id w25so38121669otm.11
for <user#domain.tld>; Mon, 19 Nov 2018 12:13:54 -0800 (PST)
A user connected to mail-ot1-x333.google.com, posted a message for user#domain.tld. The SMTP server added the Received header that appears at the bottom. Then, it sent the message to domain.tld, and Postfix server at domain.tld added the header that appears on top.
From RFC 5321, the Received headers are always added on top.
An Internet mail program MUST NOT change or delete a Received: line
that was previously added to the message header section. SMTP
servers MUST prepend Received lines to messages; they MUST NOT change
the order of existing lines or insert Received lines in any other
location.
The last one should always be the one indicating the SMTP server the user connected to.
Note that there are good reasons to have a From domain that does not match the SMTP server used for sending the message.:
ISP forces users to use their own SMTP server
Using several email accounts with a mail client that only offers a songle SMTP configuration

Sending an alias(spoof) for the 'from' e-mail using python smtplib

I am using python 3.4.3 to send e-mail, and at this time I will be needing the e-mail to be sent under an alias. The account is a gmail account, but I need to be able to put whatever I want as the spoof(alias) 'From' e-mail. I have looked quite hard at how to do this and have had very little luck. Given the amount of threads I've looked at and the actuality that I haven't gotten a workable answer shows the lack of discussion about this specific topic. I hope it's not just that this is something so very easy that everyone but me knows how to do it.
I should mention that I am on a windows 10 machine, but have access to a Ubuntu, and Windows 7 machine as well.
import smtplib
fromreal = 'realmail#gmail.com'
fromshow = 'fakemail#gmail.com'
toaddy = ['rec01#gmail.com', 'rec02#gmail.com']
subject = ' test'
body = 'This is the body test'
content = '''\
From: %s
To: %s
Subject: %s
%s
''' % (fromshow, ', '.join(toaddy), subject, body)
server = 'smtp.gmail.com'
port = 587
mail = smtplib.SMTP(server, port)
mail.ehlo()
mail.starttls()
mail.login(fromreal, 'password')
try:
mail.sendmail(fromshow, toaddy, content)
print('E-mail sent.')
except:
print('E-mail not sent.')
mail.close()
You can use yagmail to send an alias (not changing to the fake email, but at least the alias):
import yagmail
# first is "from" arg; using a dictionary you can give an alias as value
yag=yagmail.SMTP({fromreal:'fakealias'}, 'password')
yag.send(toaddy, subject, body)
How nice it is to have 3 lines instead of 30 ;)
Install using pip install yagmail.
Read more about a lot of other features on the github page.
Among other things, you could use "passwordless" scripts (no need for password in script), really easy to send HTML, inline images and attachments!
Full disclosure: I'm the developer/maintainer of yagmail.
Your code is fine,
Google prevents you from setting an alias email that not belongs to you. That's why you need to set the alias in your gmail account. To do this go to
https://mail.google.com/ -> settings -> (see all settings) -> Accounts -> Send mail as: -> add another email address.
Validate the email address and then you can set your alias as used in your code.
If you get an SMTPAuthenticationError (534, b'5.7.9 Application-specific password required. ...) you should follow the link to set an app-password instead of your real password.

Can someone please explain this code to me (Python [Sending Emails])

I have found the following code below from a website explaining how to send emails (Hotmail/live) using python. I can understand some of the code but most of it is totally confusing. For example what does, s.starttls() mean/do and also msg.as_string(); was that defined earlier or what does it do? Please help me out because i want to understand this code (i want to make a gui application version of this soon).
Guide me through this please....
P.S --> I'm only 16 so please make it sound as understandable as possible.
The Code:
import email
import smtplib
msg = email.message_from_string('warning')
msg['From'] = "example#hotmail.fr"
msg['To'] = "example#hotmail.fr"
msg['Subject'] = "helOoooOo"
s = smtplib.SMTP("smtp.live.com",587)
s.ehlo()
s.starttls()
s.ehlo()
s.login('example#hotmail.fr', 'pass')
s.sendmail("example#hotmail.fr", "example#hotmail.fr", msg.as_string())
s.quit()
I'm sorry to tell you, but this is not exactly the place to get a coding lesson. This is a place to ask for precise coding questions. So it's very likely that your question will get flagged and I will be amongst those who will ask for closing it. But…
…I've been there, learning to code at your age at a time when Internet was just a small portion of what it is today. So let me give you some insight and pointers.
The code you pasted is very straightforward, given you know a bit of programming and understand the SMTP protocol.
When you send a mail, you're doing a TCP connection to a server on ­— usually — the port 25 (but 587 in your case). There you have to follow a precise protocol to have the server understand what you want to do. This protocol has been defined by the RFC821.
The same way you follow a protocol when you meet someone (which is like "hi!", then shake hands, then say "how're you doing?") before asking a question, the server does the same:
EHLO example.org
MAIL FROM alice#example.org
RCPT TO bob#example.org
DATA
Date: Fri, 21 Aug 2015 23:12:29 +0000
From: Alice <alice#example.org>
To: Bob <bob#example.org>
Go RTFM!
--
Bob
.
Where you say to the server:
Hi! I'm Alice!
I Want to send a message to Bob!
Can you tell him to go RTFM?
Which is what you're actually doing in your code.
Now let's get more on the programming side of things. First of all, you import modules, one for handling an email the other one for the smtp connection:
import email
import smtplib
to understand how to use it, you can launch the python REPL on the command line and do dir(email) to show its objects, and help(email) to get the integrated help:
python
>>> import email
>>> dir(email)
['Charset', 'Encoders', 'Errors', 'FeedParser', 'Generator', 'Header', 'Iterators', 'LazyImporter', 'MIMEAudio', 'MIMEBase', 'MIMEImage', 'MIMEMessage', 'MIMEMultipart', 'MIMENonMultipart', 'MIMEText', 'Message', 'Parser', 'Utils', '_LOWERNAMES', '_MIMENAMES', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__version__', '_name', 'base64MIME', 'email', 'importer', 'message_from_file', 'message_from_string', 'mime', 'quopriMIME', 'sys']
>>> help(email)
Help on package email:
NAME
email - A package for parsing, handling, and generating email messages.
FILE
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/email/__init__.py
MODULE DOCS
http://docs.python.org/library/email
…
the objects are all the actions and objects being developed by the community that you can use. And the help is giving a bit more insight, as well as the URL you want to visit to know more about what you can do with it, along with examples.
Then, you create a mail message:
msg = email.message_from_string('warning')
msg['From'] = "example#hotmail.fr"
msg['To'] = "example#hotmail.fr"
msg['Subject'] = "helOoooOo"
on which you setup the Subject, From and To lines, as well as the body, in the SMTP protocol that would look like:
From: example#hotmail.fr
To: example#hotmail.fr
Subject: helOoooOo
warning
.
Then you open a connection to the SMTP server of Live.com:
s = smtplib.SMTP("smtp.live.com",587)
that connection gives an object knowing the details of where you're connecting to, and offers methods you can apply on that connection. The methods are actions you can do on it, i.e.:
You say "hi!"
s.ehlo()
Then, you start encrypting the communication:
s.starttls()
it is doing the "encryption handshake" protocol on your smtp connection, so that further communication is now "secure", i.e. nobody can read your password by looking at the packets between your computer and the server.
You can read more about it there:
https://docs.python.org/3/library/smtplib.html#smtplib.SMTP.starttls
And you can read its source code here:
https://hg.python.org/cpython/file/3.4/Lib/smtplib.py#l654
You say "hi!" again, not only because you're very polite, but because you switched from unencrypted to encrypted, your mail server is expecting you to say hi again:
s.ehlo()
You identify yourself, so that live.com knows that you're who you're prentending to be:
s.login('example#hotmail.fr', 'pass')
so there you want to change for your actual account's email address and password
Then, you send your mail to/from example#hotmail.fr:
s.sendmail("example#hotmail.fr", "example#hotmail.fr", msg.as_string())
In the first part of my answer, I told you about the "SMTP" protocol, where you have to say who you are and who you want to talk to? well this is what you're doing here. This method will translate into:
MAIL FROM example#hotmail.fr
RCPT TO example#hotmail.fr
And then, your mail object is a python "instance" that has no sense for your smtp server. You have to have it translated from the form that your mail server can understand, which is full text. You can have a look at what it is by doing in the python REPL:
python
>>> import email
>>> msg = email.message_from_string('warning')
>>> msg['From'] = "example#hotmail.fr"
>>> msg['To'] = "example#hotmail.fr"
>>> msg['Subject'] = "helOoooOo"
>>> print(msg)
<email.message.Message instance at 0x10dcb1560>
which means "python knows about an object called msg, which is made thanks to the class email.message.Message and lives at the memory address 0x10dcb1560".
But if you do the following, then you'll get the mail you'll send:
>>> print(msg.as_string())
From: example#hotmail.fr
To: example#hotmail.fr
Subject: helOoooOo
warning
Which is what actually you give to the SMTP handler instance that takes care of sending it properly.
And because you're polite, you're telling the server you're leaving by saying "bye", instead of leaving silently.
s.quit()
All that being said, I can only advice you to open a few books, take some online courses and most importantly do practice, starting from the simple stupid and boring examples to stuff way more complex. Start first by doing textual interfaces before doing graphical ones which are a bit more complex.
Here are a few pointers:
Official python tutorial: https://docs.python.org/3.5/tutorial/
Another tutorial: http://sthurlow.com/python/
You might want to follow online courses as well:
https://www.coursera.org/course/programming1
https://www.coursera.org/course/interactivepython1
https://www.udemy.com/python-for-beginners/
https://www.codecademy.com/tracks/python
just to give a few ones.
As your code suggests, it looks like you might be french, then I can only advice you to read the excellent book on algorithmic written by a former teacher I had, which is teaching programming principles and thinking without being tied to a programming language:
Passeport pour l'argorithmique object, Jean-Pierre Fournier
HTH
So message_from_string('warning') takes in a string and puts into a message object structure. Then you set the From, To, Subject. You then define the SMTP address and port it uses. ehlo() just tells the server that you're going to be sending a message through it. starttls() says that from now on, all the data sent will be sent through TLS, so all your passwords will be safe and encrypted. You login, send the mail to and get the message you previously set the first line, back as a string to send off. Then you close the SMTP connection.
All of the resources you need can be found at the Python Standard Library: 18.1 Email and 20.12 smtplib.
I will do a line by line explanation focusing on the two lines you mentioned. (Skipping the imports)
msg = email.message_from_string('warning')
Creating a message object with value "warning"
msg['From'] = "example#hotmail.fr"
msg['To'] = "example#hotmail.fr"
msg['Subject'] = "helOoooOo"
Designates Sender, Reciever, and Subject of message
s = smtplib.SMTP("smtp.live.com",587)
This sets up a mail client (specifically an SMTP client session object) that can send mail to any internet machine with an an SMTP or ESMTP listener daemon. (This includes all big emails like gmail, yahoo, aol, etc.)
s.ehlo()
This identifies you as a server using EHLO and sets up a lot of default attributes needed to send your message.
s.starttls()
Put the SMTP connection in TLS (Transport Layer Security) mode. All SMTP commands that follow will be encrypted.
s.ehlo()
Good practice to put after the previous command because the information is now encrypted.
s.login('example#hotmail.fr', 'pass')
Logs in with (username, password)
s.sendmail("example#hotmail.fr", "example#hotmail.fr", msg.as_string())
Sends the message (From, To, Msg). The msg.as_string() simply means you want the message format to be a string.
s.quit()
Terminates session.

Email sent via sendmail showing wrong 'from' address when sent via text message

I have a cronjob that runs a python script to periodically send an email with status updates to me. On the weekend I prefer this to work via text, so I send it to myphonenumber#vtext.com. This works fine, except the 'from' address is incorrect when it goes to the phone. It shows correctly when it is sent to a regular email address. For some reason when it goes through a text it shows the 'from' address as "myusername#servername.wndowsdomain.local". This may simply be a verizon text message thing specifically, but if possible I'd like to figure out how to fix it as I hope to add some reply functionality to this tool.
The machine that the script is on is running Ubuntu 12.04.
Here is the simple python function I have that sends the email:
def sendMail(to,text):
msg = MIMEMultipart('alternative')
msg['Subject'] = "Weekend Report"
msg['From'] = 'servername#mydomain.com'
msg['To'] = to
msg.attach(MIMEText(text,'plain'))
p = Popen(["/usr/sbin/sendmail", "-t"], stdin=PIPE)
p.communicate(msg.as_string())
Would I be better off using something like smtplib and a gmail account or something perhaps?
Sendmail: Setting envelope sender
You may try to set "envelope sender" address too (sender address used in SMTP session).
p=Popen(["/usr/sbin/sendmail","-t","-i","-fservername#mydomain.com"],stdin=PIPE)
Comment: I have added missing -i command line option unrelated to setting envelope sender.

Categories

Resources