请参阅本帖子结尾的最终解决方案。其余内容是解决问题所需的步骤。
我尝试使用Python 3.4.1连接仅支持TLS 1.2的FTP服务器。
你怎么知道的?
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:598)
我建议检查客户端和服务器之间的许多SSL问题,例如服务器不支持TLS 1.2,没有共同的密码等。这些问题很难调试,因为您只会收到一些SSL警报或服务器将在没有明显原因的情况下关闭连接。如果您可以访问服务器,请查看服务器端的错误消息。
您还可以尝试不强制执行SSL版本,而是使用默认值,以便客户端和服务器将同意双方支持的最佳SSL版本。如果这仍然无法正常工作,请尝试使用已知与此服务器配合工作的客户端,并进行好的和坏的连接的数据包捕获并进行比较。如果需要帮助,请将数据包捕获发布到cloudshark.org。
编辑#1:刚刚尝试了Python 3.4.0和3.4.2与测试服务器:
Python 3.4.0执行TLS 1.0握手,即忽略设置
Python 3.4.2执行成功的TLS 1.2握手
在两个版本中,ftplib存在一个小错误,即如果ftps.ssl_version不是TLS 1.0(即SSLv3或TLS1.1.+),则发送AUTH SSL而不是AUTH TLS。虽然我怀疑这不是问题的起源,但如果FTP服务器处理AUTH TLS和AUTH SSL有所不同,则可能实际上是问题的起源。
编辑#2和解决方案:
数据包捕获显示设置ftps.ssl_version没有效果,SSL握手仍将仅使用TLS 1.0进行。查看3.4.0中ftplib的源代码:
ssl_version = ssl.PROTOCOL_TLSv1
def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
certfile=None, context=None,
timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
....
if context is None:
context = ssl._create_stdlib_context(self.ssl_version,
certfile=certfile,
keyfile=keyfile)
self.context = context
由于__init__
在调用ftplib.FTP_TLS()
时被调用,因此SSL上下文将使用ftplib (ssl.PROTOCOL_TLSv1
) 默认使用的ssl_version
创建,而不是您自己的版本。要强制使用其他SSL版本,您必须提供具有所需SSL版本的自己的上下文。以下内容适用于我:
import ftplib
import ssl
ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1_2)
ftps = ftplib.FTP_TLS(context=ctx)
print (ftps.connect('108.61.166.122',31000))
print(ftps.login('test','test123'))
ftps.prot_p()
print (ftps.retrlines('LIST'))
或者,您可以全局设置协议版本,而不仅仅是为此FTP_TLS对象设置:
ftplib.FTP_TLS.ssl_version = ssl.PROTOCOL_TLSv1_2
ftps = ftplib.FTP_TLS()
还有一个小但重要的观察:看起来ftplib 不进行任何证书验证,因为它接受这个自签名证书,而不会抱怨与名称不匹配。这使得主动的中间人攻击成为可能。希望他们在未来修复这种不安全的行为,这样这里的代码将因为无效的证书而失败。