PHP скручиваемость FTP с явным TLS / SSL

Я пытался подключиться к удаленному FTP с явным сервером TLS / SSL, и время соединения истекло. Он подключается, но я не знаю, где он останавливается, но я предполагаю, что это связано с каналом управления 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)

Вот вывод локона.

*   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
)

4

Решение

Очевидно, что curl пытается использовать неявный FTP (поскольку он инициализирует сеанс TLS / SSL даже до того, как с сервером будут обмениваться какие-либо команды FTP).

Это потому, что вы указали 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
...
);

curl_setopt($ch, CURLOPT_URL, sprintf("%s/%s", $server_data['post_url'], $filename));
curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_ALL); // Enable TLS/SSL

Обратите внимание, что CURLOPT_FTP_SSL устарел и является псевдонимом CURLOPT_USE_SSL, Таким образом, нет смысла устанавливать оба.

4

Другие решения

Попробуйте удалить s из ftps: // Я установил тестовый сервер и увидел ту же ошибку с ftps vs ftp.

   'post_url' => "ftp://ftps.widgetsltd.com",
2

Я получаю то же сообщение об ошибке, что и вы, пытаясь заставить мое соединение FTPS работать с PHP и cURL. Я получаю ошибку: 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;
?>
2