You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]

原因分析

  1. 认证流程异常:从MyClient日志可见,curl优先尝试了SSH公钥认证,失败后直接断开连接,没有尝试MyHost支持的password或keyboard-interactive认证方式。
  2. 系统环境差异:Ubuntu上的curl/libssh2组件默认优先使用本地~/.ssh目录下的公钥文件进行认证,且旧版本组件存在认证失败后不 fallback 到其他方式的问题;而Windows环境下的curl/libssh2没有这个行为,会按MyHost提供的认证方法顺序尝试。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 23:10:19