使用pysftp验证主机密钥


77

我正在使用pysftp编写程序,它想针对验证SSH主机密钥C:\Users\JohnCalvin\.ssh\known_hosts

终端程序使用PuTTY将其保存到Registry中[HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys]

如何调和pysftp和PuTTY之间的区别?

我的代码是:

import pysftp as sftp

def push_file_to_server():
    s = sftp.Connection(host='138.99.99.129', username='root', password='*********')
    local_path = "testme.txt"
    remote_path = "/home/testme.txt"

    s.put(local_path, remote_path)
    s.close()

push_file_to_server()

我收到的错误响应是:

E:\ Program Files(x86)\ Anaconda3 \ lib \ site-packages \ pysftp__init __。py:61:UserWarning:
无法从C:\ Users \ JohnCalvin.ssh \ known_hosts加载HostKeys。
您将需要显式加载HostKeys(cnopts.hostkeys.load(filename))或disableHostKey检查(cnopts.hostkeys = None)。warnings.warn(wmsg,UserWarning)追溯(最近一次调用最近):push_file_to_server()中第14行的文件“ E:\ OneDrive \ Python \ GIT \ DigitalCloud \ pysftp_tutorial.py”文件“ E:\ OneDrive \ Python \ GIT \ DigitalCloud \ pysftp_tutorial.py”,第7行,位于push_file_to_server s = sftp.Connection(host ='138.99.99.129',username ='root',password ='********')文件“ E :\ Program Files(x86)\ Anaconda3 \ lib \ site-packages \ pysftp__init __。py“,第132行,位于 init中 self._tconnect ['hostkey'] = self._cnopts.get_hostkey(主机)文件“ E:\ Program Files(x86)\ Anaconda3 \ lib \ site-packages \ pysftp__init __。py”,第71行,在get_hostkey中引发SSHException(“找不到主机%s的主机密钥。“%主机)paramiko.ssh_exception.SSHException:找不到主机138.99.99.129的主机密钥。在以下情况中忽略了异常:>追溯(最近一次调用为上一次):文件“ E:\ Program Files(x86)\ Anaconda3 \ lib \ site-packages \ pysftp__init __。py”,行1013,在 del self.close()文件中:\ Program Files(x86)\ Anaconda3 \ lib \ site-packages \ pysftp__init __。py“,如果self._sftp_live:则关闭。AttributeError:'Connection'对象没有属性'_sftp_live'


2
您可以在此处明确提到此问题的文档中找到问题的答案。pysftp
patryk.beza

Answers:


98

cnopts.hostkeys = None除非您不关心安全性,否则请不要设置(如第二个最重要的答案所示)。这样,您就无法防御中间人攻击


使用CnOpts.hostkeys(返回HostKeys)管理受信任的主机密钥。

cnopts = pysftp.CnOpts(knownhosts='known_hosts')

with pysftp.Connection(host, username, password, cnopts=cnopts) as sftp:

其中known_hosts包含服务器公钥,格式如下:

example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...

如果您不想使用外部文件,也可以使用

from base64 import decodebytes
# ...

keydata = b"""AAAAB3NzaC1yc2EAAAADAQAB..."""
key = paramiko.RSAKey(data=decodebytes(keydata))
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add('example.com', 'ssh-rsa', key)

with pysftp.Connection(host, username, password, cnopts=cnopts) as sftp:

尽管从pysftp 0.2.9开始,此方法将发出警告,似乎是一个错误:
使用pysftp连接到SFTP服务器时出现“无法加载HostKeys”警告


以这种格式检索主机密钥的一种简单方法是使用OpenSSH ssh-keyscan

$ ssh-keyscan example.com
# example.com SSH-2.0-OpenSSH_5.3
example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...

(由于pysftp中的错误,如果服务器使用非标准端口,则此方法将不起作用–该条目以[example.com]:port+开头,请注意重定向ssh-keyscan到PowerShell中的文件

您还可以使应用程序自动执行相同的操作:
将Paramiko AutoAddPolicy与pysftp一起使用
(它将自动将新主机的主机密钥添加到known_hosts,但是对于已知的主机密钥,它将不接受更改的密钥)


尽管出于绝对的安全性考虑,但是如果您尚未受到攻击,则不能确保不能远程远程获取主机密钥。

请参阅我的文章如何获得SSH主机密钥指纹来授权服务器?
它用于我的WinSCP SFTP客户端,但是那里的大多数信息通常都是有效的。


如果仅需要使用指纹来验证主机密钥,请参阅Python-pysftp / paramiko-使用指纹来验证主机密钥


我应该怎么使用的hostname在参数cnopts.hostkeys.add()当它出现在known_hosts|1|xyzxyzxyz=|abcabcabc=
marengaz

1
@marengaz使用您在中使用的主机名pysftp.Connection
Martin Prikryl

84

一种选择是禁用主机密钥要求:

import pysftp
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None   
with pysftp.Connection(host, username, password, cnopts=cnopts) as sftp:
    sftp.put(local_path, remote_path)

您可以在这里找到有关此的更多信息:https : //stackoverflow.com/a/38355117/1060738

重要的提示:

设置后,cnopts.hostkeys=None您将失去针对中间人攻击的保护。使用@ martin-prikryl答案可以避免这种情况。


2
是的,这就是我最终解决问题的方式。但是以这种方式执行只会忽略主机密钥。我希望能够通过读取实际密钥来保留安全性(如果可能)。
加布里埃尔·西奥多洛斯

6
快速说明。如果您的路径中有一个空的known_hosts文件,CnOpts()将失败。
汤米·斯特兰德


我已将hostkeys设置为None,但这在docker容器内不起作用,发出警告并且未建立连接。
罗尼特

13

尝试使用0.2.8版本的pysftp库。 $ pip uninstall pysftp && pip install pysftp==0.2.8

并尝试与此:

try:
    ftp = pysftp.Connection(host, username=user, password=password)
 except:
    print("Couldn't connect to ftp")
    return False

为什么这个?基本上是pysftp的0.2.9的错误,这里是所有详细信息 https://github.com/Yenthe666/auto_backup/issues/47


1
您能详细说明一下吗?我浏览了该链接,但实际上没有找到任何合理的解释来说明为什么降级会有所帮助。虽然您的陈述“这里是所有细节”表明应该有一些。
Martin Prikryl '18年

精彩!!我已经安装了0.2.9。这个帖子挽救了我的一天!
Ram Dwivedi

5

如果您尝试通过pysftp连接到“普通” FTP,则必须将主机密钥设置为“无”。

import pysftp

cnopts = pysftp.CnOpts()
cnopts.hostkeys = None 
with pysftp.Connection(host='****',username='****',password='***',port=22,cnopts=cnopts) as sftp:
    print('DO SOMETHING')

3

编写本书以使用pysftp.CnOpts()和hostkeys选项的不同方式。

来源:https : //pysftp.readthedocs.io/en/release_0.2.9/cookbook.html

默认情况下启用主机密钥检查。默认情况下,它将使用〜/ .ssh / known_hosts。如果要禁用主机密钥检查(不建议),则需要修改默认的CnOpts并将.hostkeys设置为None。

import pysftp
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
with pysftp.Connection('host', username='me', password='pass', cnopts=cnopts):
    # do stuff here

要使用完全不同的known_hosts文件,可以在实例化时通过指定文件来覆盖CnOpts,以查找〜/ .ssh / known_hosts。

import pysftp
cnopts = pysftp.CnOpts(knownhosts='path/to/your/knownhostsfile')

with pysftp.Connection('host', username='me', password='pass', cnopts=cnopts):
    # do stuff here

如果您希望使用〜/ .ssh / known_hosts但添加其他已知主机密钥,则可以使用.load方法与更新其他known_host格式文件合并。

import pysftp
cnopts = pysftp.CnOpts()
cnopts.hostkeys.load('path/to/your/extra_knownhosts')
with pysftp.Connection('host', username='me', password='pass', cnopts=cnopts):
    # do stuff here

1

首先使用使用known_hosts文件的Windows ssh客户端连接到服务器。PuTTy会将数据存储在Windows注册表中,但是OpenSSH使用known_hosts文件,并且在您连接后会在其中添加条目。该文件的默认位置是%USERPROFILE%.ssh。我希望这有帮助


不公平的评论。你读过这个问题吗?如何调和pysftp和PuTTY之间的区别?
汉尼斯·劳滕巴赫


0

我已经auto_add_keypysftp github fork中实现了。

auto_add_keyknown_hosts如果auto_add_key=True
主机中存在密钥,则将添加该密钥known_hosts

请参考Martin Prikryl- >有关安全问题的答案

尽管出于绝对的安全性考虑,但是如果您尚未受到攻击,则不能确保不能远程远程获取主机密钥。

import pysftp as sftp

def push_file_to_server():
    s = sftp.Connection(host='138.99.99.129', username='root', password='pass', auto_add_key=True)
    local_path = "testme.txt"
    remote_path = "/home/testme.txt"

    s.put(local_path, remote_path)
    s.close()

push_file_to_server()

注意:为什么要使用上下文管理器

import pysftp
with pysftp.Connection(host, username="whatever", password="whatever", auto_add_key=True) as sftp:
    #do your stuff here
#connection closed

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.