PHP Curl无法连接Windows OpenSSH SFTP服务器问题排查
问题描述
- MyClient(PHP7.4 + Ubuntu20)可正常连接其他SFTP服务器,但无法连接MyHost(Windows Server2016 + OpenSSH SFTP)
- 其他客户端(含Windows机器上运行相同PHP代码)均能成功连接MyHost
- 日志显示认证失败,看似密码错误,但该密码在其他客户端可正常使用;认证采用keyboard-interactive方式,成功连接的客户端同样使用该方式
相关PHP代码
// Create connection string. $connection_handle = curl_init($this->protocol .'://' . $this->server . ':' . $this->port . $this->remote_directory . '/test.txt'); // Create a test file. $file_content = 'We the people of the United States of America in order to form a more perfect union:'; $file_handle = fopen('php://temp', 'r+'); fwrite($file_handle, $file_content); rewind($file_handle); if (!$file_handle) { throw new APIException('Unable to create memory file handle.'); } // Set up curl_setopt($connection_handle, CURLOPT_USERPWD, $this->user_id . ':' . $this->password); curl_setopt($connection_handle, CURLOPT_UPLOAD, true); curl_setopt($connection_handle, CURLOPT_PROTOCOLS, $this->protocol == 'sftp' ? CURLPROTO_SFTP : CURLPROTO_FTP); curl_setopt($connection_handle, CURLOPT_INFILE, $file_handle); curl_setopt($connection_handle, CURLOPT_INFILESIZE, strlen($file_content)); curl_setopt($connection_handle, CURLOPT_VERBOSE, true); curl_setopt($connection_handle, CURLOPT_CONNECTTIMEOUT, 5); // Using a self signed cert locally. curl_setopt($connection_handle, CURLOPT_SSL_VERIFYHOST, FALSE); $response = curl_exec($connection_handle);
日志信息
MyClient日志
* Trying MyHost * TCP_NODELAY set * Connected to MyHost (MyHost) port 22 (#0) * User: wsftptest * Authentication using SSH public key file * Authentication failure * Closing connection 0
MyHost日志
debug3: userauth_finish: failure partial=0 next methods="publickey,password,keyboard-interactive" [preauth] debug1: userauth-request for user MyHostUser service ssh-connection method keyboard-interactive [preauth] debug1: attempt 1 failures 0 [preauth] debug2: input_userauth_request: try method keyboard-interactive [preauth] debug1: keyboard-interactive devs [preauth] debug1: auth2_challenge: user=wsftptest devs= [preauth] debug1: kbdint_alloc: devices '' [preauth] debug2: auth2_challenge_start: devices [preauth] debug3: user_specific_delay: user specific delay 0.000ms [preauth] debug3: ensure_minimum_time_since: elapsed 0.000ms, delaying 5.311ms (requested 5.311ms) [preauth] debug3: userauth_finish: failure partial=0 next methods="publickey,password,keyboard-interactive" [preauth] debug3: send packet: type 51 [preauth] debug3: receive packet: type 1 [preauth] Received disconnect from MyHost port 38392:11: Bye Bye [preauth] Disconnected from authenticating user MyHostUser MyHost port 38392 [preauth]
原因分析
- 认证流程异常:从MyClient日志可见,curl优先尝试了SSH公钥认证,失败后直接断开连接,没有尝试MyHost支持的
password或keyboard-interactive认证方式。 - 系统环境差异:Ubuntu上的curl/libssh2组件默认优先使用本地
~/.ssh目录下的公钥文件进行认证,且旧版本组件存在认证失败后不 fallback 到其他方式的问题;而Windows环境下的curl/libssh2没有这个行为,会按MyHost提供的认证方法顺序尝试。 - keyboard-interactive处理差异:虽然指定了
CURLOPT_USERPWD,但Ubuntu环境下的libssh2可能没有正确触发keyboard-interactive流程,而是卡在公钥认证失败的环节。
解决方法
方法1:强制指定认证方式
在curl配置中添加CURLOPT_SSH_AUTH_TYPES,强制只尝试密码和键盘交互认证,跳过公钥认证:
// 新增该行,指定认证类型 curl_setopt($connection_handle, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PASSWORD | CURLSSH_AUTH_KEYBOARD_INTERACTIVE);
方法2:移除默认公钥干扰
如果MyClient本地~/.ssh目录下存在默认公钥文件(如id_rsa、id_dsa),可临时重命名或删除,避免curl自动尝试公钥认证:
mv ~/.ssh/id_rsa ~/.ssh/id_rsa.bak mv ~/.ssh/id_rsa.pub ~/.ssh/id_rsa.pub.bak
方法3:升级组件版本
Ubuntu20的curl/libssh2版本可能较旧,存在认证流程bug,通过升级组件修复:
sudo apt update && sudo apt upgrade curl libssh2-1 php-curl
内容的提问来源于stack exchange,提问作者danielson317
相关产品推荐
相关产品推荐

