Ошибка cURL 35: gnutls_handshake() не удалось

Я запускаю следующую ошибку из PHP-компонента, который использует CURL для запроса URI через SSL:

cURL error 35: gnutls_handshake() failed: A TLS packet with unexpected length was received.

Эта ошибка возникает в среде traviscc.org, но не в любой из тестовых сред. См. Travis-ci build 144663700.

Я выяснил, что версия PHP, работающая в рабочем месте Travis, снова скомпилирована "GnuTLS/2.12.14" на "Ubuntu 12.04.5 LTS" или с "GnuTLS/2.12.23" на "Ubuntu 14.04.3 LTS".

В наших средах разработки мы используем стандартные пакеты, скомпилированные против "OpenSSL/1.0.1t" в Debian (различные версии).

Поэтому я предполагаю, что проблема связана с "GnuTLS/2.12.14" или "GnuTLS/2.12.23" или с параметрами, с которыми они были скомпилированы.

Я попытался ограничить версии SSL с константой CURL CURLOPT_SSLVERSION, но это не решает проблему.

Согласно www.ssllabs.com, ведущий, о котором идет речь, - api.reporting.cloud - поддерживает TLS 1.2, TLS 1.1 и TLS 1.0.

У кого-нибудь есть намеки или указатели для меня?

Ответ 1

Обходной путь к этой проблеме заключается в настройке travis-ci для использования стандартных пакетов Ubuntu Trusty php5-cli и php5-curl. Стандартные пакеты содержат константу CURL_SSLVERSION_TLSv1_1.

Файл .travis.yml выглядит следующим образом:

sudo: required

dist: trusty

language: php

before_install:
  - sudo apt-get -y install git zip php5-cli php5-curl

before_script:
  - php -r "printf('PHP %s', phpversion());"
  - composer self-update
  - composer install --no-interaction

script:
  - mkdir -p ./build/logs
  - ./vendor/bin/phpunit

В исходном PHP это просто вопрос установки вышеупомянутой константы в случае выполнения PHP-кода travis-ci:

if (getenv('TRAVIS')) {
    $options['curl'][CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_1;
}

Это обходное решение имеет тот недостаток, что он работает только на конкретной версии PHP, которую предлагает Ubuntu Trusty (PHP 5.5). Учитывая, что PHP 5.5 достигли конца жизни 10 июля 2016 года, это решение неприемлемо.

Было бы идеально для travis-ci обновить Ubuntu 16.04 LTS, но Брэндон Бертон, менеджер по инфраструктуре в travis-ci написал в феврале 28, 2016:

Учитывая, что мы в настоящее время сосредоточены на поддержке 12.04 и 14.04, первичные среды. На данный момент маловероятно, что мы будем поддерживая 16.04 в качестве родной среды в этом году.

Таким образом, казалось бы, мы застряли с Ubuntu Trusty некоторое время.

Корень этой проблемы состоит в том, что версия PHP, которая работает на travis-ci, была скомпилирована с gnutls-cli (GnuTLS) 2.12.23, с 2011 года. У этой конкретной версии gnutls-cli есть проблемы с некоторыми (но не все ) TLS 1.2.

@travis-ci: Можно ли перекомпилировать версии PHP, которые вы используете, с более современной версией GnuTLS - или, по крайней мере, с поддержкой TLS 1.2?

Ответ 2

В PHP можно управлять протоколом SSL, который скручивается с константами CURL_SSLVERSION_ *.

Установив:

curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1);

Я могу заставить curl использовать "TLS 1.1".

Установив:

curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);

Я могу заставить curl использовать "TLS 1.0".

Чтобы проверить все возможные протоколы SSL, я создал следующий script, который затем выполняется travis-ci:

<?php

$sslVersions = [
    CURL_SSLVERSION_DEFAULT,
    CURL_SSLVERSION_TLSv1,
    CURL_SSLVERSION_TLSv1_0,
    CURL_SSLVERSION_TLSv1_1,
    CURL_SSLVERSION_TLSv1_2,
    CURL_SSLVERSION_SSLv2,
    CURL_SSLVERSION_SSLv3,
];

var_dump(curl_version());

foreach ($sslVersions as $sslVersion) {

    $uri = "https://api.reporting.cloud";

    printf("Trying %d", $sslVersion);
    echo PHP_EOL;

    $ch = curl_init($uri);

    curl_setopt($ch, CURLOPT_VERBOSE        , true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , 0);
    curl_setopt($ch, CURLOPT_TIMEOUT        , 2);
    curl_setopt($ch, CURLOPT_SSLVERSION     , $sslVersion);

    if (curl_exec($ch) === false) {
        var_dump(curl_error($ch));
    } else {
        curl_close($ch);
    }

    echo PHP_EOL;
    echo PHP_EOL;

}

exit(1);

Вывод этого script в моих средах разработки:

array(9) {
  ["version_number"]=>
  int(468480)
  ["age"]=>
  int(3)
  ["features"]=>
  int(182173)
  ["ssl_version_number"]=>
  int(0)
  ["version"]=>
  string(6) "7.38.0"
  ["host"]=>
  string(19) "x86_64-pc-linux-gnu"
  ["ssl_version"]=>
  string(14) "OpenSSL/1.0.1t"
  ["libz_version"]=>
  string(5) "1.2.8"
  ["protocols"]=>
  array(21) {
    [0]=>
    string(4) "dict"
    [1]=>
    string(4) "file"
    [2]=>
    string(3) "ftp"
    [3]=>
    string(4) "ftps"
    [4]=>
    string(6) "gopher"
    [5]=>
    string(4) "http"
    [6]=>
    string(5) "https"
    [7]=>
    string(4) "imap"
    [8]=>
    string(5) "imaps"
    [9]=>
    string(4) "ldap"
    [10]=>
    string(5) "ldaps"
    [11]=>
    string(4) "pop3"
    [12]=>
    string(5) "pop3s"
    [13]=>
    string(4) "rtmp"
    [14]=>
    string(4) "rtsp"
    [15]=>
    string(3) "scp"
    [16]=>
    string(4) "sftp"
    [17]=>
    string(4) "smtp"
    [18]=>
    string(5) "smtps"
    [19]=>
    string(6) "telnet"
    [20]=>
    string(4) "tftp"
  }
}
Trying 0
* Rebuilt URL to: https://api.reporting.cloud/
* Hostname was NOT found in DNS cache
*   Trying 40.76.93.116...
* Connected to api.reporting.cloud (40.76.93.116) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-SHA384
* Server certificate:
*    subject: serialNumber=HRB 25927; 1.3.6.1.4.1.311.60.2.1.3=DE; businessCategory=Private Organization; C=DE; postalCode=28215; ST=Bremen; L=Bremen; street=Admiralstr. 54; O=Text Control GmbH; OU=ReportingCloud; OU=COMODO EV SSL; CN=api.reporting.cloud
*    start date: 2016-06-17 00:00:00 GMT
*    expire date: 2017-06-17 23:59:59 GMT
*    subjectAltName: api.reporting.cloud matched
*    issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Extended Validation Secure Server CA
*    SSL certificate verify ok.
> GET / HTTP/1.1
Host: api.reporting.cloud
Accept: */*

< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
* Server Microsoft-IIS/8.5 is not blacklisted
< Server: Microsoft-IIS/8.5
< X-AspNetMvc-Version: 5.2
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Fri, 15 Jul 2016 14:22:40 GMT
< Content-Length: 952
< 
* Connection #0 to host api.reporting.cloud left intact


Trying 1
* Rebuilt URL to: https://api.reporting.cloud/
* Hostname was found in DNS cache
*   Trying 40.76.93.116...
* Connected to api.reporting.cloud (40.76.93.116) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-SHA384
* Server certificate:
*    subject: serialNumber=HRB 25927; 1.3.6.1.4.1.311.60.2.1.3=DE; businessCategory=Private Organization; C=DE; postalCode=28215; ST=Bremen; L=Bremen; street=Admiralstr. 54; O=Text Control GmbH; OU=ReportingCloud; OU=COMODO EV SSL; CN=api.reporting.cloud
*    start date: 2016-06-17 00:00:00 GMT
*    expire date: 2017-06-17 23:59:59 GMT
*    subjectAltName: api.reporting.cloud matched
*    issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Extended Validation Secure Server CA
*    SSL certificate verify ok.
> GET / HTTP/1.1
Host: api.reporting.cloud
Accept: */*

< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
* Server Microsoft-IIS/8.5 is not blacklisted
< Server: Microsoft-IIS/8.5
< X-AspNetMvc-Version: 5.2
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Fri, 15 Jul 2016 14:22:40 GMT
< Content-Length: 952
< 
* Connection #0 to host api.reporting.cloud left intact


Trying 4
* Rebuilt URL to: https://api.reporting.cloud/
* Hostname was found in DNS cache
*   Trying 40.76.93.116...
* Connected to api.reporting.cloud (40.76.93.116) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.0 / ECDHE-RSA-AES256-SHA
* Server certificate:
*    subject: serialNumber=HRB 25927; 1.3.6.1.4.1.311.60.2.1.3=DE; businessCategory=Private Organization; C=DE; postalCode=28215; ST=Bremen; L=Bremen; street=Admiralstr. 54; O=Text Control GmbH; OU=ReportingCloud; OU=COMODO EV SSL; CN=api.reporting.cloud
*    start date: 2016-06-17 00:00:00 GMT
*    expire date: 2017-06-17 23:59:59 GMT
*    subjectAltName: api.reporting.cloud matched
*    issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Extended Validation Secure Server CA
*    SSL certificate verify ok.
> GET / HTTP/1.1
Host: api.reporting.cloud
Accept: */*

< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
* Server Microsoft-IIS/8.5 is not blacklisted
< Server: Microsoft-IIS/8.5
< X-AspNetMvc-Version: 5.2
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Fri, 15 Jul 2016 14:22:40 GMT
< Content-Length: 952
< 
* Connection #0 to host api.reporting.cloud left intact


Trying 5
* Rebuilt URL to: https://api.reporting.cloud/
* Hostname was found in DNS cache
*   Trying 40.76.93.116...
* Connected to api.reporting.cloud (40.76.93.116) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.1 / ECDHE-RSA-AES256-SHA
* Server certificate:
*    subject: serialNumber=HRB 25927; 1.3.6.1.4.1.311.60.2.1.3=DE; businessCategory=Private Organization; C=DE; postalCode=28215; ST=Bremen; L=Bremen; street=Admiralstr. 54; O=Text Control GmbH; OU=ReportingCloud; OU=COMODO EV SSL; CN=api.reporting.cloud
*    start date: 2016-06-17 00:00:00 GMT
*    expire date: 2017-06-17 23:59:59 GMT
*    subjectAltName: api.reporting.cloud matched
*    issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Extended Validation Secure Server CA
*    SSL certificate verify ok.
> GET / HTTP/1.1
Host: api.reporting.cloud
Accept: */*

< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
* Server Microsoft-IIS/8.5 is not blacklisted
< Server: Microsoft-IIS/8.5
< X-AspNetMvc-Version: 5.2
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Fri, 15 Jul 2016 14:22:41 GMT
< Content-Length: 952
< 
* Connection #0 to host api.reporting.cloud left intact


Trying 6
* Rebuilt URL to: https://api.reporting.cloud/
* Hostname was found in DNS cache
*   Trying 40.76.93.116...
* Connected to api.reporting.cloud (40.76.93.116) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-SHA384
* Server certificate:
*    subject: serialNumber=HRB 25927; 1.3.6.1.4.1.311.60.2.1.3=DE; businessCategory=Private Organization; C=DE; postalCode=28215; ST=Bremen; L=Bremen; street=Admiralstr. 54; O=Text Control GmbH; OU=ReportingCloud; OU=COMODO EV SSL; CN=api.reporting.cloud
*    start date: 2016-06-17 00:00:00 GMT
*    expire date: 2017-06-17 23:59:59 GMT
*    subjectAltName: api.reporting.cloud matched
*    issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Extended Validation Secure Server CA
*    SSL certificate verify ok.
> GET / HTTP/1.1
Host: api.reporting.cloud
Accept: */*

< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
* Server Microsoft-IIS/8.5 is not blacklisted
< Server: Microsoft-IIS/8.5
< X-AspNetMvc-Version: 5.2
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Fri, 15 Jul 2016 14:22:41 GMT
< Content-Length: 952
< 
* Connection #0 to host api.reporting.cloud left intact


Trying 2
* Rebuilt URL to: https://api.reporting.cloud/
* Hostname was found in DNS cache
*   Trying 40.76.93.116...
* Connected to api.reporting.cloud (40.76.93.116) port 443 (#0)
* OpenSSL was built without SSLv2 support
* Closing connection 0
string(39) "OpenSSL was built without SSLv2 support"


Trying 3
* Rebuilt URL to: https://api.reporting.cloud/
* Hostname was found in DNS cache
*   Trying 40.76.93.116...
* Connected to api.reporting.cloud (40.76.93.116) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* Unknown SSL protocol error in connection to api.reporting.cloud:443 
* Closing connection 0
string(68) "Unknown SSL protocol error in connection to api.reporting.cloud:443 "

Здесь мы можем четко видеть, что "SSL-соединение с использованием TLSv1.0" правильно подключается к серверу.

Тем не менее, запуск того же script на travi-ci приводит к следующему:

PHP Notice:  Use of undefined constant CURL_SSLVERSION_TLSv1_0 - assumed 'CURL_SSLVERSION_TLSv1_0' in /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php on line 7
PHP Stack trace:
PHP   1. {main}() /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php:0

Notice: Use of undefined constant CURL_SSLVERSION_TLSv1_0 - assumed 'CURL_SSLVERSION_TLSv1_0' in /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php on line 7

Call Stack:
    0.0002     241400   1. {main}() /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php:0

PHP Notice:  Use of undefined constant CURL_SSLVERSION_TLSv1_1 - assumed 'CURL_SSLVERSION_TLSv1_1' in /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php on line 8
PHP Stack trace:
PHP   1. {main}() /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php:0

Notice: Use of undefined constant CURL_SSLVERSION_TLSv1_1 - assumed 'CURL_SSLVERSION_TLSv1_1' in /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php on line 8

Call Stack:
    0.0002     241400   1. {main}() /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php:0

PHP Notice:  Use of undefined constant CURL_SSLVERSION_TLSv1_2 - assumed 'CURL_SSLVERSION_TLSv1_2' in /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php on line 9
PHP Stack trace:
PHP   1. {main}() /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php:0

Notice: Use of undefined constant CURL_SSLVERSION_TLSv1_2 - assumed 'CURL_SSLVERSION_TLSv1_2' in /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php on line 9

Call Stack:
    0.0002     241400   1. {main}() /home/travis/build/TextControl/txtextcontrol-reportingcloud-php/demo/ssl-issue.php:0

array(9) {
  'version_number' =>
  int(464384)
  'age' =>
  int(3)
  'features' =>
  int(50749)
  'ssl_version_number' =>
  int(0)
  'version' =>
  string(6) "7.22.0"
  'host' =>
  string(19) "x86_64-pc-linux-gnu"
  'ssl_version' =>
  string(14) "GnuTLS/2.12.14"
  'libz_version' =>
  string(7) "1.2.3.4"
  'protocols' =>
  array(18) {
    [0] =>
    string(4) "dict"
    [1] =>
    string(4) "file"
    [2] =>
    string(3) "ftp"
    [3] =>
    string(4) "ftps"
    [4] =>
    string(6) "gopher"
    [5] =>
    string(4) "http"
    [6] =>
    string(5) "https"
    [7] =>
    string(4) "imap"
    [8] =>
    string(5) "imaps"
    [9] =>
    string(4) "ldap"
    [10] =>
    string(4) "pop3"
    [11] =>
    string(5) "pop3s"
    [12] =>
    string(4) "rtmp"
    [13] =>
    string(4) "rtsp"
    [14] =>
    string(4) "smtp"
    [15] =>
    string(5) "smtps"
    [16] =>
    string(6) "telnet"
    [17] =>
    string(4) "tftp"
  }
}
Trying 0
* About to connect() to api.reporting.cloud port 443 (#0)
*   Trying 40.76.93.116... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* gnutls_handshake() failed: A TLS packet with unexpected length was received.
* Closing connection #0
string(76) "gnutls_handshake() failed: A TLS packet with unexpected length was received."


Trying 1
* About to connect() to api.reporting.cloud port 443 (#0)
*   Trying 40.76.93.116... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* gnutls_handshake() failed: A TLS packet with unexpected length was received.
* Closing connection #0
string(76) "gnutls_handshake() failed: A TLS packet with unexpected length was received."


Trying 0
* About to connect() to api.reporting.cloud port 443 (#0)
*   Trying 40.76.93.116... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* gnutls_handshake() failed: A TLS packet with unexpected length was received.
* Closing connection #0
string(76) "gnutls_handshake() failed: A TLS packet with unexpected length was received."


Trying 0
* About to connect() to api.reporting.cloud port 443 (#0)
*   Trying 40.76.93.116... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* gnutls_handshake() failed: A TLS packet with unexpected length was received.
* Closing connection #0
string(76) "gnutls_handshake() failed: A TLS packet with unexpected length was received."


Trying 0
* About to connect() to api.reporting.cloud port 443 (#0)
*   Trying 40.76.93.116... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* gnutls_handshake() failed: A TLS packet with unexpected length was received.
* Closing connection #0
string(76) "gnutls_handshake() failed: A TLS packet with unexpected length was received."


Trying 2
* About to connect() to api.reporting.cloud port 443 (#0)
*   Trying 40.76.93.116... * connected
* GnuTLS does not support SSLv2
* Closing connection #0
string(29) "GnuTLS does not support SSLv2"


Trying 3
* About to connect() to api.reporting.cloud port 443 (#0)
*   Trying 40.76.93.116... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* gnutls_handshake() failed: A TLS packet with unexpected length was received.
* Closing connection #0
string(76) "gnutls_handshake() failed: A TLS packet with unexpected length was received."

Я также заметил, что константы CURL_SSLVERSION_TLSv1_0, CURL_SSLVERSION_TLSv1_1 и CURL_SSLVERSION_TLSv1_2 недоступны в версиях travis-ci PHP 5.6 и PHP 7.

Подводя итог, я зациклился на всех возможных константах CURL_SSLVERSION_ *, и ни один из них не позволяет мне подключаться к api.reporting.cloud на travis-ci, независимо от того, какую версию PHP я использую.

Есть ли у кого-нибудь предложения о том, как я могу подключиться к api.reporting.cloud от travis-ci?

Ответ 3

Я нашел решение проблемы в этом списке рассылки:

Сервер не любит что-то в поддержке TLS 1.2 gnutls 2.12, поскольку, если вы отключите его, он работает. Тот же сервер работает с gnutls 3.2, и единственное отличие в приветствии клиента от двух что gnutls 3.2 имеет больше возможностей.

Я использую (требуется использовать) "gnutls-cli (GnuTLS) 2.12.23".

Ниже приведена следующая ошибка:

gnutls-cli --priority "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2" api.reporting.cloud

Тем не менее, форсирование "TLS 1.1" или "TLS 1.0" возвращает как ожидалось:

gnutls-cli --priority "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.1" api.reporting.cloud
gnutls-cli --priority "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0" api.reporting.cloud

Следующий шаг - сделать этот параметр с PHP через CURL (в конкретном случае с неправильной версией библиотеки).