OXY NOTES

curl_setoptでSSL connect errorが出て接続に失敗する場合の対処法

とあるWebサービスでPHPのcURLを利用してスクレイピングをしていたのですが、突然エラーが出るようになりました。
エラーを再現をしてみるとhttpsのサイトへ暗号化通信する際にエラーが出るようです。
curl_error()」でエラーを出力してみると「SSL connect errorと出力されます。
試しに別のサーバで同様のプログラムを実行すると正常に終了します。どうやらサーバの環境が原因のようです。

以前、同じエラーが出力された際は、以下のオプションを付けて対処しました。

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

これはサーバの証明書を検証しないオプションで、オレオレ証明書などを使ってSSL通信しているサイトへアクセスする際に有効な対策です。(もちろんセキュリティ上のリスクは上がる)

今回はこれでは改善しません。
そこで似た症状を検索していると、暗号化通信に利用するNSS(Network Security Services)が古いことが原因で同様の症状が出るとのこと。早速バージョンを調べてみます。

# yum info nss

Installed Packages
Name        : nss
Arch        : x86_64
Version     : 3.19.1
Release     : 8.el6_7
Size        : 2.6 M
Repo        : installed
From repo   : updates
Summary     : Network Security Services
URL         : http://www.mozilla.org/projects/security/pki/nss/
License     : MPLv2.0
Description : Network Security Services (NSS) is a set of libraries designed to
            : support cross-platform development of security-enabled client and
            : server applications. Applications built with NSS can support SSL
            : v2 and v3, TLS, PKCS #5, PKCS #7, PKCS #11, PKCS #12, S/MIME,
            : X.509 v3 certificates, and other security standards.
(省略)
Name        : nss
Arch        : x86_64
Version     : 3.36.0
Release     : 9.el6_10
Size        : 865 k
Repo        : updates
Summary     : Network Security Services
URL         : http://www.mozilla.org/projects/security/pki/nss/
License     : MPLv2.0
Description : Network Security Services (NSS) is a set of libraries designed to
            : support cross-platform development of security-enabled client and
            : server applications. Applications built with NSS can support SSL
            : v2 and v3, TLS, PKCS #5, PKCS #7, PKCS #11, PKCS #12, S/MIME,
            : X.509 v3 certificates, and other security standards.

どうやら今入ってるのが3.19.1で、最新は3.36.0とのこと。
調べてみると3.19.1は最新のTLS1.2に対応していないとのこと。これではAWSなどTLS1.2を利用したサイトへ接続できません。

早速アップデートします。

# yum update nss
(省略)
Dependencies Resolved

================================================================================
 Package            Arch          Version                  Repository      Size
================================================================================
Updating:
 nss                x86_64        3.36.0-9.el6_10          updates        865 k
Updating for dependencies:
 nspr               x86_64        4.19.0-1.el6             base           114 k
 nss-sysinit        x86_64        3.36.0-9.el6_10          updates         53 k
 nss-tools          x86_64        3.36.0-9.el6_10          updates        460 k
 nss-util           x86_64        3.36.0-1.el6             base            72 k

Transaction Summary
================================================================================
Upgrade       5 Package(s)

Total download size: 1.5 M
Is this ok [y/N]: y ← yを押してエンター
(省略)
Complete!

無事インストール完了で治るかと思いきやエラーが出続けています。
TLS handshakeの状態を見るべく「--verbose」オプションを付けてコマンド上で実行します。(ドメインやIPはダミーです)

# curl --verbose --head https://example.com/
* About to connect() to example.com port 443 (#0)
*   Trying 198.164.0.0... connected
* Connected to example.com (198.164.0.0) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* NSS error -12286
* Closing connection #0
* SSL connect error
curl: (35) SSL connect error

NSS error」や「SSL connect error」と出ています。
ひょっとするとnssだけでなくcurlも古いのかもと、見てみます。

# yum info curl

Installed Packages
Name        : curl
Arch        : x86_64
Version     : 7.19.7
Release     : 46.el6
Size        : 348 k
Repo        : installed
From repo   : base
Summary     : A utility for getting files from remote servers (FTP, HTTP, and
            : others)
URL         : http://curl.haxx.se/
License     : MIT
Description : cURL is a tool for getting files from HTTP, FTP, FILE, LDAP,
            : LDAPS, DICT, TELNET and TFTP servers, using any of the supported
            : protocols. cURL is designed to work without user interaction or
            : any kind of interactivity. cURL offers many useful capabilities,
            : like proxy support, user authentication, FTP upload, HTTP post,
            : and file transfer resume.

Available Packages
Name        : curl
Arch        : x86_64
Version     : 7.19.7
Release     : 53.el6_9
Size        : 197 k
Repo        : base
Summary     : A utility for getting files from remote servers (FTP, HTTP, and
            : others)
URL         : http://curl.haxx.se/
License     : MIT
Description : cURL is a tool for getting files from HTTP, FTP, FILE, LDAP,
            : LDAPS, DICT, TELNET and TFTP servers, using any of the supported
            : protocols. cURL is designed to work without user interaction or
            : any kind of interactivity. cURL offers many useful capabilities,
            : like proxy support, user authentication, FTP upload, HTTP post,
            : and file transfer resume.

Versionの7.19.7は同じですが、Releaseは46.el653.el6_9でかなりバージョンが違うようです。
古いものに固執する理由もないので、アップデートします。

# yum update curl
(省略)
Dependencies Resolved

================================================================================
 Package          Arch            Version                   Repository     Size
================================================================================
Updating:
 curl             x86_64          7.19.7-53.el6_9           base          197 k
Updating for dependencies:
 libcurl          x86_64          7.19.7-53.el6_9           base          169 k

Transaction Summary
================================================================================
Upgrade       2 Package(s)

Total download size: 367 k
Is this ok [y/N]: y
(省略)
Complete!

そして動作するかチェック。

# curl --verbose --head https://example.com/
* About to connect() to example.com port 443 (#0)
*   Trying 198.164.0.0... connected
* Connected to example.com (198.164.0.0) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*       subject: CN=*.example.com
*       start date: Sep 15 00:00:00 2018 GMT
*       expire date: Oct 15 12:00:00 2019 GMT
*       common name: *.example.com
*       issuer: CN=Amazon,OU=Server CA 1B,O=Amazon,C=US
> HEAD / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: example.com
> Accept: */*
>

無事「HTTP/1.1 200 OK」となってます。(ちなみにこのサンプルはAWSを利用したサイトの場合です)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256」となっているのでやはりTLS1.2への対応が必要だったみたいです。

ちなみにコマンドラインで成功してもPHPで失敗する場合は単純にキャッシュの問題かもしれません。
私の場合は半日ほど放置していたらPHPでも正常にスクレイピングできるようになりました。


というわけで、curl_getinfo($ch, CURLINFO_HTTP_CODE)を設定したり、NSSをアップデートしても「SSL connect error」が出る場合はcurl自体のアップデートも必要かもしれないよ。という投稿でした。