我想用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是脚本中的变量。 我有两个问题:
[编辑]
@tintin解释说,ssl.create_default_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_default_context()
的一些代码。这些设置合适吗?
注意:在我最初问题的代码中是一个错误。以下是相关行的正确版本:
smtp_server.starttls(context=context)
[\Edit]
长话短说:如果不检查
.starttls()
的响应代码,可以从smtplib<;=py3.5.1rc1<;=py2.7.10中剥离starttls在支持它的smtp服务器上显式调用
.starttls()
,使用恶意MitM剥离您的STARTTLS
命令并伪造非220
响应不会协商ssl,也不会引发异常,因此保持通信未加密-因此,除非手动验证对.starttls()[0]==220
的响应或内部.sock
的响应已被ssl包装,否则它容易受到striptls的攻击。下面是一个python 2.7.9smtplib通信示例,它与您的示例类似,通过让服务器或MitM应答
999 NOSTARTTLS
而不是200
协商starttls失败。客户端脚本中没有显式检查200响应代码,没有由于starttls尝试失败而导致的异常,因此邮件传输未加密:在不支持STARTTLS的smtp服务器上显式调用
.starttls()
或MitM从服务器响应中剥离此功能将引发SMTPNotSupportedError
。见下面的代码。一般注意:加密还取决于配置的cipherspec,即SSLContext,在您的情况下,它是由
ssl.create_default_context()
创建的。注意,将SSLContext配置为允许进行身份验证但不加密的cipherspec(如果服务器和客户端都提供/允许)是完全有效的。E、 g.TLS_RSA_WITH_NULL_SHA256
。NULL-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=None Mac=SHA256
根据this answerpythonpre 2.7.9/3.4.3的说法,不尝试对默认ssl上下文执行证书验证,因此容易被ssl截获。从Python开始2.7.9/3.4.3对默认上下文执行证书验证。这也意味着您必须手动启用2.7.9/3.4.3之前版本的证书验证(通过创建自定义sslcontext),否则可能会接受任何不受信任的证书。
.sendmail()
、.send_message
和.starttls()
将隐式调用.ehlo_or_helo_if_needed()
,因此无需再次显式调用它。这也是请参见下面的source::smtplib::starttls (cpython, inofficial github):
相关问题 更多 >
编程相关推荐