Paramiko 是一个用 Python 编写的 SSHv2 协议实现库,用于安全地进行远程连接、执行命令、传输文件(SFTP)等操作

📅 2026/7/15 5:09:55 👁️ 阅读次数 📝 编程学习
Paramiko 是一个用 Python 编写的 SSHv2 协议实现库,用于安全地进行远程连接、执行命令、传输文件(SFTP)等操作

Paramiko 是一个用 Python 编写的 SSHv2 协议实现库,用于安全地进行远程连接、执行命令、传输文件(SFTP)等操作。它不依赖外部 SSH 二进制程序(如 OpenSSH),纯 Python 实现,跨平台兼容性好,广泛应用于自动化运维、网络设备管理、CI/CD 脚本等场景。

核心功能包括:

  • 建立 SSH 连接(支持密码、私钥、密钥口令等多种认证方式)
  • 执行远程 Shell 命令并获取标准输出/错误
  • 使用 SFTP 子系统上传/下载文件、管理远程目录
  • 支持通道(Channel)复用、会话保持、超时与异常处理

基础示例(密码登录执行命令):

importparamiko client=paramiko.SSHClient()client.set_missing_host_key_policy(paramiko.AutoAddPolicy())# 自动接受未知主机密钥(生产环境应校验)try:client.connect(hostname='192.168.1.100',username='user',password='pass',timeout=10)stdin,stdout,stderr=client.exec_command('uptime')print(stdout.read().decode())finally:client.close()

使用私钥登录示例:

private_key=paramiko.RSAKey.from_private_key_file('/path/to/id_rsa',password='key_pass')# 若私钥有密码client.connect(hostname='192.168.1.100',username='user',pkey=private_key)

SFTP 文件上传示例:

sftp=client.open_sftp()sftp.put('local.txt','/remote/local.txt')sftp.close()

⚠️ 安全提示:

  • 生产环境中避免AutoAddPolicy(),应使用RejectPolicy()并预置可信 host key;
  • 敏感凭证(密码、私钥)建议通过环境变量或密钥管理服务加载;
  • 始终显式关闭连接(close())或使用with上下文管理器(需封装适配)。
    在 Paramiko 中安全验证远程服务器的 SSH 主机密钥,是防范中间人攻击(MITM)的核心措施。绝不能使用AutoAddPolicy()生产环境,而应显式校验主机密钥是否可信。以下是推荐的安全实践:

✅ 正确做法:使用paramiko.MissingHostKeyPolicy的子类或预置已知主机密钥

方案一:基于已知 hosts 文件(推荐,类 OpenSSH 行为)

importparamiko# 加载已知主机密钥(类似 ~/.ssh/known_hosts)host_keys=paramiko.HostKeys()host_keys.load('/path/to/known_hosts')# 可为 OpenSSH 格式文件client=paramiko.SSHClient()client.set_missing_host_key_policy(paramiko.RejectPolicy())# 拒绝未知主机client.set_host_keys(host_keys)try:client.connect(hostname='192.168.1.100',username='user',key_filename='/id_rsa')print("✅ 连接成功,主机密钥已验证")exceptparamiko.BadHostKeyExceptionase:print(f"❌ 主机密钥不匹配:{e}")exceptparamiko.AuthenticationException:print("❌ 认证失败")exceptExceptionase:print(f"❌ 连接异常:{e}")finally:client.close()

方案二:首次连接时手动确认并持久化(交互式安全初始化)

defconnect_with_manual_verification(hostname,username,**kwargs):client=paramiko.SSHClient()client.set_missing_host_key_policy(paramiko.RejectPolicy())try:client.connect(hostname,username,**kwargs)returnclientexceptparamiko.SSHExceptionase:if"No hostkey for server"instr(e)orisinstance(e,paramiko.BadHostKeyException):# 获取远程主机密钥(不验证时临时连接)transport=paramiko.Transport((hostname,22))transport.start_client()remote_key=transport.get_remote_server_key()fingerprint=remote_key.get_fingerprint().hex()# SHA256 hex(Paramiko ≥ 2.10)print(f"⚠️ 首次连接{hostname},密钥指纹:{fingerprint}")confirm=input("是否信任该密钥?(y/N): ").strip().lower()ifconfirm=='y':# 手动添加到 known_hostshost_keys=paramiko.HostKeys()host_keys.add(hostname,remote_key.get_name(),remote_key)host_keys.save('/path/to/known_hosts')transport.close()returnconnect_with_manual_verification(hostname,username,**kwargs)else:raiseRuntimeError("用户拒绝信任主机密钥")raisee

方案三:硬编码可信公钥(适用于固定、受控环境)

expected_key=paramiko.RSAKey(data=bytes.fromhex("..."))# 从已验证的服务器导出的公钥 hexclient=paramiko.SSHClient()client.set_missing_host_key_policy(paramiko.RejectPolicy())try:client.connect(hostname='192.168.1.100',username='user',pkey=private_key)# 手动校验transport=client.get_transport()remote_key=transport.get_remote_server_key()ifremote_key.asbytes()!=expected_key.asbytes():raiseValueError("❌ 主机密钥与预期不符!")exceptExceptionase:print(e)

🔍 补充建议:

  • 使用transport.get_remote_server_key().get_name()获取密钥类型(ssh-rsa,ecdsa-sha2-nistp256,ssh-ed25519
  • Paramiko ≥ 2.10 默认使用 SHA256 指纹;旧版本用.get_fingerprint()返回 MD5(不安全,已弃用)
  • 可结合ssh-keyscan提前批量获取可信密钥:ssh-keyscan -t rsa,ecdsa,ed25519 host >> known_hosts

✅ 总结:安全 =拒绝未知 + 显式信任 + 持久化验证,而非自动接受。