web-dev-qa-db-ja.com

cURLエラー35:gnutls_handshake()が失敗しました

SSL経由でURIを要求するためにCURLを使用するPHPコンポーネントから、次のエラーが発生しています。

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

このエラーはtravis-ci.org環境で発生しますが、テスト環境では発生しません。 travis-ci build 1446637 を参照してください。

Travisワーカーで実行中のPHPバージョンは、「Ubuntu 12.04.5 LTS」で「GnuTLS/2.12.14」、または「GnuTLS/2.12.23」でコンパイルされていることを発見しました「Ubuntu 14.04.3 LTS」。

開発環境では、Debian(さまざまなバージョン)の「OpenSSL/1.0.1t」に対してコンパイルされた標準パッケージを使用します。

したがって、問題は「GnuTLS/2.12.14」または「GnuTLS/2.12.23」、またはそれらがコンパイルされたパラメーターに関連していると思います。

CURL定数CURLOPT_SSLVERSIONでSSLバージョンを制限しようとしましたが、それでも問題は解決しません。

Www.ssllabs.comによると、問題のホスト-api.reporting.cloud-はTLS 1.2、TLS 1.1およびTLS 1.0をサポートしています。

誰かが私に何かヒントや指針を持っていますか?

13
Jonathan Maron

この問題の回避策は、標準のUbuntu Trusty php5-cliおよびphp5-curlパッケージを使用するようにtravis-ciを構成することです。標準パッケージは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ソースでは、travis-ciによってPHPコードが実行されている場合、前述の定数を設定するだけです。

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

この回避策には、Ubuntu Trustyが提供する特定のPHPバージョン(PHP 5.5)でのみ機能するという欠点があります。PHP 5.5が7月にサポート終了に達した2016年10月、このソリューションは受け入れられません。

Travis-ciはUbuntu 16.04 LTSに更新するのが理想的ですが、2016年2月28日のTravis-ci wrote のインフラストラクチャマネージャーであるBrandon Burtonは次のようになります。

そのため、現在、主要な環境として12.04と14.04のサポートに重点を置いています。現時点では、今年のネイティブ環境として16.04をサポートすることはほとんどありません。

したがって、しばらくの間、Ubuntu Trustyに悩まされているように見えます。

この問題の根本は、travis-ciで実行されるPHPバージョンが2011年からgnutls-cli(GnuTLS)2.12.23でコンパイルされていることです。この特定のバージョンのgnutls-cliには問題があります。一部(すべてではない)のTLS 1.2接続。

@ travis-ci:より最近のバージョンのGnuTLSに対してPHPバージョンを使用して、またはTLS 1.2をより適切にサポートするバージョンに対して)再コンパイルすることは可能でしょうか?

7
Jonathan Maron

PHPでは、curlがCURL_SSLVERSION_ *定数で使用するSSLプロトコルを制御できます。

設定することにより:

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プロトコルをテストするために、次のスクリプトを作成し、それを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);

私の開発環境でのこのスクリプトの出力は次のとおりです。

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 "

ここでは、「TLSv1.0を使用したSSL接続」がバックエンドサーバーに正しく接続していることがはっきりとわかります。

ただし、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_ *定数をループしましたが、使用しているPHPバージョンに関係なく、1つの定数ではtravis-ciのapi.reporting.cloudに接続できません。

Travis-ciからapi.reporting.cloudに接続する方法について何か提案はありますか?

4
Jonathan Maron

私はこの問題の解決策をこの メーリングリスト で見つけました:

サーバーはgnutls 2.12のTLS 1.2サポートに何かを好まない。なぜならそれを無効にすると機能するように見えるからだ。同じサーバーがgnutls 3.2で動作し、2つのバージョンのclient helloの唯一の違いは、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

次のステップは、CURLを介してPHPからこの設定を行うことです(ライブラリバージョンの特定の場合)。

1
Jonathan Maron