問題描述
我嘗試連接到僅支持 TLS 1.2 的 FTP 服務(wù)器使用 Python 3.4.1
I try to connect to a FTP Server which only supports TLS 1.2 Using Python 3.4.1
我的代碼:
import ftplib
import ssl
ftps = ftplib.FTP_TLS()
ftps.ssl_version = ssl.PROTOCOL_TLSv1_2
print (ftps.connect('108.61.166.122',31000))
print(ftps.login('test','test123'))
ftps.prot_p()
print (ftps.retrlines('LIST'))
客戶端錯(cuò)誤:
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:598)
服務(wù)器端出錯(cuò):
Failed TLS negotiation on control channel, disconnected. (SSL_accept():
error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol)
示例中的憑據(jù)正在用于測(cè)試.
The credentials in the example are working for testing.
推薦答案
最終解決方案見文末.其余的是調(diào)試問題所需的步驟.
See the end of this post for the final solution. The rest are the steps needed to debug the problem.
我嘗試使用 Python 3.4.1 連接到僅支持 TLS 1.2 的 FTP 服務(wù)器
I try to connect to a FTP Server which only supports TLS 1.2 Using Python 3.4.1
你怎么知道的?
ssl.SSLEOFError: EOF 發(fā)生違反協(xié)議 (_ssl.c:598)
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:598)
我會(huì)建議客戶端和服務(wù)器之間的許多 SSL 問題之一,例如服務(wù)器不支持 TLS 1.2、沒有通用密碼等.這些問題很難調(diào)試,因?yàn)槟粗皇盏揭恍?SSL 警報(bào),要么服務(wù)器將簡(jiǎn)單地關(guān)閉沒有任何明顯原因的連接.如果您有權(quán)訪問服務(wù)器,請(qǐng)?jiān)诜?wù)器端查找錯(cuò)誤消息.
I would suggest one of the many SSL problems between client and server, like the server not supporting TLS 1.2, no common ciphers etc. These problems are hard to debug because you either get only some SSL alert or the server will simply close the connection without any obvious reason. If you have access to the server look for error messages on the server side.
您也可以嘗試不強(qiáng)制使用 SSL 版本,而是使用默認(rèn)版本,以便客戶端和服務(wù)器同意兩者都支持的最佳 SSL 版本.如果這仍然不起作用,請(qǐng)嘗試使用已知與該服務(wù)器一起工作的客戶端,并捕獲好連接和壞連接的數(shù)據(jù)包并進(jìn)行比較.如果您需要有關(guān)該帖子的幫助,請(qǐng)將數(shù)據(jù)包捕獲發(fā)送到 cloudshark.org.
You may also try to not to enforce an SSL version but use the default instead, so that client and server will agree to the best SSL version both support. If this will still not work try with a client which is known to work with this server and make a packet capture of the good and bad connections and compare. If you need help with that post the packet captures to cloudshark.org.
Edit#1:剛剛在測(cè)試服務(wù)器上使用 python 3.4.0 和 3.4.2 進(jìn)行了嘗試:
Edit#1: just tried it with python 3.4.0 and 3.4.2 against a test server:
- python 3.4.0 執(zhí)行 TLS 1.0 握手,即忽略設(shè)置
- python 3.4.2 成功與 TLS 1.2 握手
在這兩個(gè)版本中,ftplib 都有一個(gè)小錯(cuò)誤,如果 ftps.ssl_version
是別的東西,它會(huì)發(fā)送 AUTH SSL
而不是 AUTH TLS
TLS 1.0,即 SSLv3 或 TLS1.1.+.雖然我懷疑這是問題的根源,但實(shí)際上可能是 FTP 服務(wù)器以不同方式處理 AUTH TLS
和 AUTH SSL
.
In both versions ftplib has the minor bug, that it sends AUTH SSL
instead of AUTH TLS
if ftps.ssl_version
is something else then TLS 1.0, i.e. SSLv3 or TLS1.1.+. While I doubt that this is the origin of the problem it might actually be if the FTP server handles AUTH TLS
and AUTH SSL
differently.
編輯#2 和解決方案:
數(shù)據(jù)包捕獲顯示設(shè)置 ftps.ssl_version
無效,SSL 握手仍將僅使用 TLS 1.0 完成.查看 3.4.0 中 ftplib 的源代碼:
A packet capture shows that setting ftps.ssl_version
has no effect and the SSL handshake will still be done with TLS 1.0 only. Looking at the source code of ftplib in 3.4.0 gives:
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
由于在調(diào)用 ftplib.FTP_TLS()
時(shí)調(diào)用了 __init__
,因此 SSL 上下文將使用 ftplib 使用的默認(rèn) ssl_version
創(chuàng)建(ssl.PROTOCOL_TLSv1
) 而不是你自己的版本.要強(qiáng)制執(zhí)行另一個(gè) SSL 版本,您必須為您自己的上下文提供所需的 SSL 版本.以下對(duì)我有用:
Since __init__
is called when ftplib.FTP_TLS()
is called the SSL context will be created with the default ssl_version
used by ftplib (ssl.PROTOCOL_TLSv1
) and not with your own version. To enforce another SSL version you must to provide your own context with the needed SSL version. The following works for me:
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'))
或者,您可以全局設(shè)置協(xié)議版本,而不僅僅是為此 FTP_TLS 對(duì)象:
Alternatively you could set the protocol version globally instead of only for this FTP_TLS object:
ftplib.FTP_TLS.ssl_version = ssl.PROTOCOL_TLSv1_2
ftps = ftplib.FTP_TLS()
還有一個(gè)小而重要的觀察:看起來 ftplib 不進(jìn)行任何類型的證書驗(yàn)證,因?yàn)樗邮芰诉@個(gè)與名稱不匹配的自簽名證書而不會(huì)抱怨.這使得主動(dòng)中間人攻擊成為可能.希望他們將來能修復(fù)這種不安全的行為,在這種情況下,這里的代碼會(huì)因?yàn)樽C書無效而失敗.
And just a small but important observation: it looks like that ftplib does not do any kind of certificate validation, since it accepts this self-signed certificate which does not match the name without complaining. This makes a active man-in-the-middle attack possible. Hopefully they will fix this insecure behavior in the future, in which case the code here will fail because of an invalid certificate.
這篇關(guān)于使用 ftplib 連接到 FTP TLS 1.2 服務(wù)器的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!