使用STARTTLS从Python发送电子邮件

使用STARTTLS从Python发送电子邮件,python,email,encryption,smtplib,starttls,Python,Email,Encryption,Smtplib,Starttls,我想使用Python的smtplib发送带有Python脚本的电子邮件 如果可以建立到服务器的加密连接,脚本应该只发送电子邮件。 要加密到端口587的连接,我想使用STARTTLS 通过一些示例,我编写了以下代码: smtp_server = smtplib.SMTP(host, port=port) context = ssl.create_default_context() smtp_server.starttls(context) smtp_server.login(user, p

我想使用Python的smtplib发送带有Python脚本的电子邮件

如果可以建立到服务器的加密连接,脚本应该只发送电子邮件。 要加密到端口587的连接,我想使用STARTTLS

通过一些示例,我编写了以下代码:

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是脚本中的变量。 我有两个问题:

  • 连接始终是加密的还是易受STRIPTLS攻击()
  • 我应该使用SMTP对象的ehlo()方法吗?在某些示例中,在调用starttls()之前和之后显式调用它。另一方面,在smptlib的文档中,如果有必要,sendmail()将调用它
[编辑]

@丁丁解释说,
ssl.create\u default\u context()
可能会导致连接不安全。因此,我使用以下示例更改了代码:

_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)
对于密码设置,我使用了最新版本的
ssl.create\u default\u context()
的一些代码。这些设置是否合适

注意:在我的原始问题代码中有一个错误。以下是相关行的正确版本:

smtp_server.starttls(context=context)
[\Edit]

连接始终是加密的还是易受STRIPTLS攻击()


长话短说:starttls可以从smtplib中剥离出来,非常感谢您提供了这个详细的答案。现在,我使用以下代码创建ssl.Context:`Context=ssl.SSLContext(ssl.PROTOCOL\u SSLv23)Context.options |=ssl.OP\u NO\u SSLv2 Context.options |=ssl.OP\u NO\u SSLv3 Context.set\u ciphers(\u DEFAULT\u ciphers)Context.set\u DEFAULT\u verify\u path()context.verify_mode=ssl.CERT_REQUIRED`我可以得到一个示例代码来将TLSv1.2与smtplib一起使用吗。这是我的项目的一个要求,我找不到做同样事情的正确信息我正在使用Python 2.7.18。
220 xx ESMTP
250-xx
250-SIZE 20480000
250-AUTH LOGIN
250-STARTTLS
250 HELP
STARTTLS
999 NOSTARTTLS
mail FROM:<a@b.com> size=686
250 OK
rcpt TO:<a@b.com>
250 OK
data
def starttls(self, keyfile=None, certfile=None, context=None):
    """Puts the connection to the SMTP server into TLS mode.

    If there has been no previous EHLO or HELO command this session, this
    method tries ESMTP EHLO first.

    If the server supports TLS, this will encrypt the rest of the SMTP
    session. If you provide the keyfile and certfile parameters,
    the identity of the SMTP server and client can be checked. This,
    however, depends on whether the socket module really checks the
    certificates.

    This method may raise the following exceptions:

     SMTPHeloError            The server didn't reply properly to
                              the helo greeting.
    """
    self.ehlo_or_helo_if_needed()
    if not self.has_extn("starttls"):
        raise SMTPNotSupportedError(
            "STARTTLS extension not supported by server.")
    (resp, reply) = self.docmd("STARTTLS")
    if resp == 220:
        if not _have_ssl:
            raise RuntimeError("No SSL support included in this Python")
        if context is not None and keyfile is not None:
            raise ValueError("context and keyfile arguments are mutually "
                             "exclusive")
        if context is not None and certfile is not None:
            raise ValueError("context and certfile arguments are mutually "
                             "exclusive")
        if context is None:
            context = ssl._create_stdlib_context(certfile=certfile,
                                                 keyfile=keyfile)
        self.sock = context.wrap_socket(self.sock,
                                        server_hostname=self._host)
        self.file = None
        # RFC 3207:
        # The client MUST discard any knowledge obtained from
        # the server, such as the list of SMTP service extensions,
        # which was not obtained from the TLS negotiation itself.
        self.helo_resp = None
        self.ehlo_resp = None
        self.esmtp_features = {}
        self.does_esmtp = 0
    return (resp, reply)