PHP通过SSH隧道访问SFTP/SSH的认证失败问题求助
问题描述
我的PHP服务器无法直接连接互联网,必须通过AdminServer建立SSH隧道才能访问外部SFTP/SSH服务器。在Bash中使用ssh -J命令可以成功建立跳转连接,但PHP代码实现时遇到问题——通过ssh2_tunnel创建隧道后,用ssh2_auth_password认证目标SFTP/SSH账户时触发错误:
PHP Warning: ssh2_auth_password(): supplied resource is not a valid SSH2 Session resource
导致认证失败。查阅ssh2_sftp相关文档未找到隧道相关解决方案,特此求助。
初始SFTP测试代码
<?php $connection = ssh2_connect("172.16.0.2", 22); // AdminServer if(ssh2_auth_password($connection, "user", "password")) { if ($tunnel = ssh2_tunnel($connection, "sftpservice.domain.com", 22)) // SFTP server { echo "tunnel ok"; // 如何认证SFTP? if (ssh2_auth_password($tunnel, "sftp_user", "sftp_password")) { echo "auth sftp"; } else { echo "passwd tunnel fail"; } } else { echo "Tunnel creation failed.\n"; } } else { echo "failed!"; } ?>
初始测试结果
- tunnel ok
- PHP Warning: ssh2_auth_password(): supplied resource is not a valid SSH2 Session resource in /var/www/sftp_test.php on line 11
- auth sftp fail
可行的Bash跳转命令
tunnel_ip='172.16.0.2' tunnel_port='22' tunnel_user='xxx'; tunnel_pwd='abc' ssh_ip='ip_addresse' ssh_port='5962' ssh_user='xyz' ssh_pwd='abc' # 此命令可正常执行 ssh -J ${tunnel_user}@${tunnel_ip} -p ${ssh_port} ${ssh_user}@${ssh_ip}
SSH连接测试PHP代码
$connection = ssh2_connect($tunnel_ip, $tunnel_port); if(ssh2_auth_password($connection, $tunnel_user, $tunnel_pwd)) { echo "* connection $tunnel_ip ok \n"; if ($tunnel = ssh2_tunnel($connection, $ssh_ip, $ssh_port)) { echo "* tunnel ok \n"; if (ssh2_auth_password($tunnel, $ssh_user, $ssh_pwd)) { echo "* auth ssh ok \n"; } else { echo "* auth ssh fail \n"; } } else { echo "* tunnel creation failed.\n"; } } else { echo "* connection $tunnel_ip fail \n"; }
SSH测试结果
- tunnel ok
- PHP Warning: ssh2_auth_password(): supplied resource is not a valid SSH2 Session resource in /var/www/test.php on line 22
- auth ssh fail
解决方案
问题根源
原生PHP ssh2扩展的ssh2_tunnel仅创建TCP层的隧道连接,返回的是流资源,而非SSH会话资源。ssh2_auth_password仅能接收ssh2_connect返回的SSH会话对象,因此直接用隧道流调用该方法会报错。
方案1:调用系统ssh命令实现端口转发
通过PHP执行系统ssh命令建立本地端口转发,再连接本地转发端口访问目标服务器,模拟ssh -J的跳转逻辑:
<?php // 配置参数 $jumpHost = '172.16.0.2'; $jumpUser = 'user'; $jumpPass = 'password'; $targetHost = 'sftpservice.domain.com'; $targetPort = 22; $targetUser = 'sftp_user'; $targetPass = 'sftp_password'; $localPort = 2222; // 本地临时转发端口 // 用sshpass实现非交互式输入密码,建立端口转发并后台运行 $cmd = "sshpass -p '$jumpPass' ssh -L $localPort:$targetHost:$targetPort $jumpUser@$jumpHost -Nf"; exec($cmd, $output, $returnVar); if ($returnVar === 0) { // 连接本地转发端口,等同于直接连接目标服务器 $connection = ssh2_connect('localhost', $localPort); if ($connection && ssh2_auth_password($connection, $targetUser, $targetPass)) { echo "SFTP认证成功\n"; // 后续可执行SFTP操作 $sftp = ssh2_sftp($connection); // 示例:读取目标文件 $file = fopen("ssh2.sftp://$sftp/path/to/file", 'r'); // ... } else { echo "SFTP认证失败\n"; } // 关闭隧道(按需保留,若长期使用可省略此步骤) exec("pkill -f 'ssh -L $localPort:$targetHost:$targetPort'"); } else { echo "建立端口转发失败\n"; } ?>
注意:需在服务器安装
sshpass工具,或提前配置PHP服务器与AdminServer的SSH免密登录,避免明文密码风险。
方案2:使用phpseclib库(推荐)
原生ssh2扩展对跳转连接支持有限,phpseclib的SSH2类原生支持ProxyJump功能,实现更简洁安全:
<?php require_once 'vendor/autoload.php'; // 需通过Composer安装 use phpseclib3\Net\SSH2; use phpseclib3\Net\SFTP; // 跳板机配置 $jumpHost = '172.16.0.2'; $jumpUser = 'user'; $jumpPass = 'password'; // 目标SFTP服务器配置 $targetHost = 'sftpservice.domain.com'; $targetUser = 'sftp_user'; $targetPass = 'sftp_password'; // 连接跳板机 $jumpSSH = new SSH2($jumpHost); if (!$jumpSSH->login($jumpUser, $jumpPass)) { exit('跳板机登录失败'); } // 通过跳板机连接目标SFTP服务器 $sftp = new SFTP($targetHost, 22, ['proxy' => $jumpSSH]); if ($sftp->login($targetUser, $targetPass)) { echo "SFTP认证成功\n"; // 示例:列出目标服务器根目录 print_r($sftp->nlist('/')); } else { echo "SFTP认证失败\n"; } ?>
安装方式:在项目目录执行
composer require phpseclib/phpseclib:^3.0
内容的提问来源于stack exchange,提问作者BeWog
相关产品推荐
相关产品推荐

