PHP 使用显式卷曲 FTPes TLS/SSL
PHP curl FTPes w/ explicit TLS/SSL
我一直在尝试使用显式 TLS/SSL 服务器推送到远程 FTP,但连接一直超时。它正在连接,但我不知道它在哪里停止,但我假设它与 FTP/TLS/SSL 控制通道有关。任何帮助将不胜感激。
通过 FileZilla,我可以毫无问题地连接,但不能通过 curl。我可能遗漏了一些东西,希望这里的人知道使用 curl 重新创建会话的最佳方法。这是 FileZilla 的调试输出和我在 PHP/curl 中处理的示例代码。这是 PHP 代码,下面是成功的 FileZilla 会话的副本。
这是版本,但我在不同的系统上试过,结果相同。
Curl: v7.43.0
└─(08:04:00 on master ✹)──> php -v ──(Thu,Sep10)─┘
PHP 5.5.27 (cli) (built: Jul 14 2015 17:04:01)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2015 Zend Technologies
with Xdebug v2.3.3, Copyright (c) 2002-2015, by Derick Rethans
这是实际代码。
<?php
$server_data = array(
'transfer_id' => 123456789,
'post_url' => "ftps://ftps.widgetsltd.com",
'port' => 21,
'username' => 'widgetsftp',
'password' => 'password',
);
$filename = sprintf("%s-%s-%s.csv",
$server_data['transfer_id'],
microtime(TRUE),
rand(1000, 9999));
$temp_filename = sprintf("/tmp/%s", $filename);
$ftp_data = "This is a test";
$fp = fopen($temp_filename, 'w');
fprintf($fp, "%s", $ftp_data);
fclose($fp);
$fp = fopen($temp_filename, 'r');
$ch = curl_init();
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
curl_setopt($ch, CURLOPT_URL, sprintf("%s/%s", $server_data['post_url'], $filename));
curl_setopt($ch, CURLOPT_PORT, 21);
curl_setopt($ch, CURLOPT_USERPWD, sprintf("%s:%s", $server_data['username'], $server_data['password']));
curl_setopt($ch, CURLOPT_UPLOAD, TRUE);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($temp_filename));
curl_setopt($ch, CURLOPT_USE_SSL, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0);
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'AES-128-CBC');
curl_setopt($ch, CURLOPT_FTP_SSL, CURLOPT_FTPSSLAUTH);
curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
print_r(array('curl_exec' => curl_exec($ch)));
print_r(array(
'curl_errno' => curl_errno($ch),
'curl_error' => curl_error($ch),
));
这是 FileZilla 会话。
Status: Resolving address of ftps.widgetsltd.com
Status: Connecting to 123.123.123.123:21...
Status: Connection established, waiting for welcome message...
Trace: CFtpControlSocket::OnReceive()
Response: 220-Microsoft FTP Service
Response: 220 Widgets, LTD FTP server
Trace: CFtpControlSocket::SendNextCommand()
Command: AUTH TLS
Trace: CFtpControlSocket::OnReceive()
Response: 234 AUTH command ok. Expecting TLS Negotiation.
Status: Initializing TLS...
Trace: CTlsSocket::Handshake()
Trace: CTlsSocket::ContinueHandshake()
Trace: CTlsSocket::OnSend()
Trace: CTlsSocket::OnRead()
Trace: CTlsSocket::ContinueHandshake()
Trace: CTlsSocket::OnRead()
Trace: CTlsSocket::ContinueHandshake()
Trace: TLS Handshake successful
Trace: Protocol: TLS1.0, Key exchange: RSA, Cipher: AES-128-CBC, MAC: SHA1
Status: Verifying certificate...
Status: TLS connection established.
Trace: CFtpControlSocket::SendNextCommand()
Command: USER s-rokfri
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 331 Password required for s-rokfri.
Trace: CFtpControlSocket::SendNextCommand()
Command: PASS ********
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 230-This service and information contained therein belong to Widgets, LTD.
Response: 230 User logged in.
Trace: CFtpControlSocket::SendNextCommand()
Command: OPTS UTF8 ON
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 200 OPTS UTF8 command successful - UTF8 encoding now ON.
Trace: CFtpControlSocket::SendNextCommand()
Command: PBSZ 0
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 200 PBSZ command successful.
Trace: CFtpControlSocket::SendNextCommand()
Command: PROT P
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 200 PROT command successful.
Status: Connected
Trace: CFtpControlSocket::ResetOperation(0)
Trace: CControlSocket::ResetOperation(0)
Trace: CFileZillaEnginePrivate::ResetOperation(0)
Trace: Measured latency of 141 ms
Status: Retrieving directory listing...
Trace: CFtpControlSocket::SendNextCommand()
Trace: CFtpControlSocket::ChangeDirSend()
Command: PWD
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 257 "/" is current directory.
Trace: CFtpControlSocket::ResetOperation(0)
Trace: CControlSocket::ResetOperation(0)
Trace: CFtpControlSocket::ParseSubcommandResult(0)
Trace: CFtpControlSocket::ListSubcommandResult()
Trace: state = 1
Trace: CFtpControlSocket::ResetOperation(0)
Trace: CControlSocket::ResetOperation(0)
Status: Directory listing of "/" successful
Trace: CFileZillaEnginePrivate::ResetOperation(0)
这是 curl 输出。
* Trying 123.123.123.123...
* Connected to ftps.widgetsltd.com (123.123.123.123) port 21 (#0)
* SSL connection timeout
* Closing connection 0
Array
(
[curl_exec] =>
)
Array
(
[curl_errno] => 28
[curl_error] => SSL connection timeout
)
尝试从 ftps 中删除 s:// 我设置了一个测试服务器并看到 ftps 与 ftp.
相同的错误
'post_url' => "ftp://ftps.widgetsltd.com",
curl 显然尝试使用隐式 FTP(因为它甚至在与服务器交换任何 FTP 命令之前就初始化 TLS/SSL 会话)。
这是因为您指定了 ftps://
前缀,用于隐式 TLS。它具有特殊前缀,因为隐式 TLS 使用特殊端口 (990)。但是你用 CURLOPT_PORT
.
覆盖了默认值
虽然显式 TLS 使用标准 FTP 端口 (21),因此它使用标准 ftp://
前缀。要启用显式 TLS,请使用 CURLOPT_USE_SSL
(您已经在做的事情,只是通过错误的值,选项类型是枚举,而不是布尔值)。
所以代码应该是:
$server_data = array(
...
'post_url' => "ftp://ftps.widgetsltd.com", // ftp:// URL
...
);
$url = sprintf("%s/%s", $server_data['post_url'], $filename);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_ALL); // Enable TLS/SSL
请注意,CURLOPT_FTP_SSL
已过时,是 CURLOPT_USE_SSL
的别名。所以,两者都设置是没有意义的。
我收到了与您尝试使用 PHP 和 cURL 建立 FTPS 连接时相同的错误消息。我收到的错误是:curl_errno 28 - SSL 连接超时。
我终于用下面的代码让它工作了。我意识到这个问题已经得到解答,但我想提供我的工作代码以帮助他人。
以下是我如何使用 PHP 和 cURL 将文件上传到 FTPS 服务器:
<?php
// !!! MAKE SURE SERVER ADDRESS STARTS WITH ftp://...
$ftp_server="ftp://ftps.example.com";
$ftp_user_name="USERNAME";
$ftp_user_pass="PASSWORD";
$localFileName = "test.txt";
$remoteFileName = "/Export/test.txt";
$fp = fopen($localFileName, 'r');
$stderr = fopen("curl.txt", "w"); //for error msg logging
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ftp_server.$remoteFileName);
curl_setopt($ch, CURLOPT_PORT, 21);
curl_setopt($ch, CURLOPT_USERPWD, "$ftp_user_name:$ftp_user_pass");
curl_setopt($ch, CURLOPT_UPLOAD, 1);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localFileName));
//SSL stuff
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //use for development only; unsecure
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //use for development only; unsecure
curl_setopt($ch, CURLOPT_FTP_SSL, CURLOPT_FTPSSLAUTH);
curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
//curl_setopt($ch, CURLOPT_SSLVERSION, 3);
//end SSL
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
curl_setopt($ch, CURLOPT_STDERR, $stderr);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_exec ($ch);
$error_no = curl_errno($ch);
$error_msg = curl_error($ch);
curl_close ($ch);
fclose($fp);
fclose($stderr);
if ($error_no == 0)
{
$status = "Success";
}
else
{
$status = "Failed";
}
echo "FTP RESULT: <BR/>error_no: ".$error_no . "<br/>msg: " . $error_msg;
?>
我一直在尝试使用显式 TLS/SSL 服务器推送到远程 FTP,但连接一直超时。它正在连接,但我不知道它在哪里停止,但我假设它与 FTP/TLS/SSL 控制通道有关。任何帮助将不胜感激。
通过 FileZilla,我可以毫无问题地连接,但不能通过 curl。我可能遗漏了一些东西,希望这里的人知道使用 curl 重新创建会话的最佳方法。这是 FileZilla 的调试输出和我在 PHP/curl 中处理的示例代码。这是 PHP 代码,下面是成功的 FileZilla 会话的副本。
这是版本,但我在不同的系统上试过,结果相同。
Curl: v7.43.0
└─(08:04:00 on master ✹)──> php -v ──(Thu,Sep10)─┘
PHP 5.5.27 (cli) (built: Jul 14 2015 17:04:01)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2015 Zend Technologies
with Xdebug v2.3.3, Copyright (c) 2002-2015, by Derick Rethans
这是实际代码。
<?php
$server_data = array(
'transfer_id' => 123456789,
'post_url' => "ftps://ftps.widgetsltd.com",
'port' => 21,
'username' => 'widgetsftp',
'password' => 'password',
);
$filename = sprintf("%s-%s-%s.csv",
$server_data['transfer_id'],
microtime(TRUE),
rand(1000, 9999));
$temp_filename = sprintf("/tmp/%s", $filename);
$ftp_data = "This is a test";
$fp = fopen($temp_filename, 'w');
fprintf($fp, "%s", $ftp_data);
fclose($fp);
$fp = fopen($temp_filename, 'r');
$ch = curl_init();
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
curl_setopt($ch, CURLOPT_URL, sprintf("%s/%s", $server_data['post_url'], $filename));
curl_setopt($ch, CURLOPT_PORT, 21);
curl_setopt($ch, CURLOPT_USERPWD, sprintf("%s:%s", $server_data['username'], $server_data['password']));
curl_setopt($ch, CURLOPT_UPLOAD, TRUE);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($temp_filename));
curl_setopt($ch, CURLOPT_USE_SSL, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0);
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'AES-128-CBC');
curl_setopt($ch, CURLOPT_FTP_SSL, CURLOPT_FTPSSLAUTH);
curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
print_r(array('curl_exec' => curl_exec($ch)));
print_r(array(
'curl_errno' => curl_errno($ch),
'curl_error' => curl_error($ch),
));
这是 FileZilla 会话。
Status: Resolving address of ftps.widgetsltd.com
Status: Connecting to 123.123.123.123:21...
Status: Connection established, waiting for welcome message...
Trace: CFtpControlSocket::OnReceive()
Response: 220-Microsoft FTP Service
Response: 220 Widgets, LTD FTP server
Trace: CFtpControlSocket::SendNextCommand()
Command: AUTH TLS
Trace: CFtpControlSocket::OnReceive()
Response: 234 AUTH command ok. Expecting TLS Negotiation.
Status: Initializing TLS...
Trace: CTlsSocket::Handshake()
Trace: CTlsSocket::ContinueHandshake()
Trace: CTlsSocket::OnSend()
Trace: CTlsSocket::OnRead()
Trace: CTlsSocket::ContinueHandshake()
Trace: CTlsSocket::OnRead()
Trace: CTlsSocket::ContinueHandshake()
Trace: TLS Handshake successful
Trace: Protocol: TLS1.0, Key exchange: RSA, Cipher: AES-128-CBC, MAC: SHA1
Status: Verifying certificate...
Status: TLS connection established.
Trace: CFtpControlSocket::SendNextCommand()
Command: USER s-rokfri
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 331 Password required for s-rokfri.
Trace: CFtpControlSocket::SendNextCommand()
Command: PASS ********
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 230-This service and information contained therein belong to Widgets, LTD.
Response: 230 User logged in.
Trace: CFtpControlSocket::SendNextCommand()
Command: OPTS UTF8 ON
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 200 OPTS UTF8 command successful - UTF8 encoding now ON.
Trace: CFtpControlSocket::SendNextCommand()
Command: PBSZ 0
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 200 PBSZ command successful.
Trace: CFtpControlSocket::SendNextCommand()
Command: PROT P
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 200 PROT command successful.
Status: Connected
Trace: CFtpControlSocket::ResetOperation(0)
Trace: CControlSocket::ResetOperation(0)
Trace: CFileZillaEnginePrivate::ResetOperation(0)
Trace: Measured latency of 141 ms
Status: Retrieving directory listing...
Trace: CFtpControlSocket::SendNextCommand()
Trace: CFtpControlSocket::ChangeDirSend()
Command: PWD
Trace: CTlsSocket::OnRead()
Trace: CFtpControlSocket::OnReceive()
Response: 257 "/" is current directory.
Trace: CFtpControlSocket::ResetOperation(0)
Trace: CControlSocket::ResetOperation(0)
Trace: CFtpControlSocket::ParseSubcommandResult(0)
Trace: CFtpControlSocket::ListSubcommandResult()
Trace: state = 1
Trace: CFtpControlSocket::ResetOperation(0)
Trace: CControlSocket::ResetOperation(0)
Status: Directory listing of "/" successful
Trace: CFileZillaEnginePrivate::ResetOperation(0)
这是 curl 输出。
* Trying 123.123.123.123...
* Connected to ftps.widgetsltd.com (123.123.123.123) port 21 (#0)
* SSL connection timeout
* Closing connection 0
Array
(
[curl_exec] =>
)
Array
(
[curl_errno] => 28
[curl_error] => SSL connection timeout
)
尝试从 ftps 中删除 s:// 我设置了一个测试服务器并看到 ftps 与 ftp.
相同的错误 'post_url' => "ftp://ftps.widgetsltd.com",
curl 显然尝试使用隐式 FTP(因为它甚至在与服务器交换任何 FTP 命令之前就初始化 TLS/SSL 会话)。
这是因为您指定了 ftps://
前缀,用于隐式 TLS。它具有特殊前缀,因为隐式 TLS 使用特殊端口 (990)。但是你用 CURLOPT_PORT
.
虽然显式 TLS 使用标准 FTP 端口 (21),因此它使用标准 ftp://
前缀。要启用显式 TLS,请使用 CURLOPT_USE_SSL
(您已经在做的事情,只是通过错误的值,选项类型是枚举,而不是布尔值)。
所以代码应该是:
$server_data = array(
...
'post_url' => "ftp://ftps.widgetsltd.com", // ftp:// URL
...
);
$url = sprintf("%s/%s", $server_data['post_url'], $filename);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_ALL); // Enable TLS/SSL
请注意,CURLOPT_FTP_SSL
已过时,是 CURLOPT_USE_SSL
的别名。所以,两者都设置是没有意义的。
我收到了与您尝试使用 PHP 和 cURL 建立 FTPS 连接时相同的错误消息。我收到的错误是:curl_errno 28 - SSL 连接超时。
我终于用下面的代码让它工作了。我意识到这个问题已经得到解答,但我想提供我的工作代码以帮助他人。
以下是我如何使用 PHP 和 cURL 将文件上传到 FTPS 服务器:
<?php
// !!! MAKE SURE SERVER ADDRESS STARTS WITH ftp://...
$ftp_server="ftp://ftps.example.com";
$ftp_user_name="USERNAME";
$ftp_user_pass="PASSWORD";
$localFileName = "test.txt";
$remoteFileName = "/Export/test.txt";
$fp = fopen($localFileName, 'r');
$stderr = fopen("curl.txt", "w"); //for error msg logging
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ftp_server.$remoteFileName);
curl_setopt($ch, CURLOPT_PORT, 21);
curl_setopt($ch, CURLOPT_USERPWD, "$ftp_user_name:$ftp_user_pass");
curl_setopt($ch, CURLOPT_UPLOAD, 1);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localFileName));
//SSL stuff
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //use for development only; unsecure
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //use for development only; unsecure
curl_setopt($ch, CURLOPT_FTP_SSL, CURLOPT_FTPSSLAUTH);
curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
//curl_setopt($ch, CURLOPT_SSLVERSION, 3);
//end SSL
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
curl_setopt($ch, CURLOPT_STDERR, $stderr);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_exec ($ch);
$error_no = curl_errno($ch);
$error_msg = curl_error($ch);
curl_close ($ch);
fclose($fp);
fclose($stderr);
if ($error_no == 0)
{
$status = "Success";
}
else
{
$status = "Failed";
}
echo "FTP RESULT: <BR/>error_no: ".$error_no . "<br/>msg: " . $error_msg;
?>