I want to send emails with a Python script by using Python's smtplib.
The script should only send the email, if an encrypted connection to the server can be established. To encrypt the connection to port 587 I want to use STARTTLS.
Using some examples I have written the following code:
smtp_server = smtplib.SMTP(host, port=port)
context = ssl.create_default_context()
smtp_server.starttls(context)
smtp_server.login(user, password)
smtp_server.send_message(msg)
msg, host, port, user, password are variables in my script. I have two questions:
- Is the connection always encrypted or is it vulnerable to the STRIPTLS attack (https://en.wikipedia.org/wiki/STARTTLS).
- Should I use the ehlo() method of the SMTP object? In some examples it is called explicitly before and after calling starttls(). On the other side in the documentation of smptlib it is written, that sendmail() will call it, if it is necessary.
[Edit]
@tintin explained, that ssl.create_default_context()
can possibly lead to insecure connections. Thus I have changed the code using some examples in the following way:
_DEFAULT_CIPHERS = (
'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:'
'!eNULL:!MD5')
smtp_server = smtplib.SMTP(host, port=port)
# only TLSv1 or higher
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
context.set_ciphers(_DEFAULT_CIPHERS)
context.set_default_verify_paths()
context.verify_mode = ssl.CERT_REQUIRED
if smtp_server.starttls(context=context)[0] != 220:
return False # cancel if connection is not encrypted
smtp_server.login(user, password)
For the cipher setting I used some code of a recent version of ssl.create_default_context()
. Are these settings appropriate?
Note: In the code of my original question is one mistake. Here is the correct version of the concerned line:
smtp_server.starttls(context=context)
[\Edit]
ssl.Context
: ` context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context.options |= ssl.OP_NO_SSLv2 context.options |= ssl.OP_NO_SSLv3 context.set_ciphers(_DEFAULT_CIPHERS) context.set_default_verify_paths() context.verify_mode = ssl.CERT_REQUIRED` – Kaleena