All Articles

PHPのcurlでSSL Connection Error.のエラーがどうしても出る時の対策

久々にどっぷりはまってしまった && ほとんど情報がなくて困ったので、解決策を備忘録として残しておこうと思います。

PHPのcurlでSSLのページにリクエストを送るとどうしてもエラーになってしまう

今回ハマったのは上記です。とあるECの案件で決済モジュールへのリクエストが必要で(当然SSL通信下で)、どれだけ設定を変えてもエラー、エラー、エラー…。ググっても情報は見つからず、何が原因かも分からないまま途方に暮れていたのですが、ふとしたことから閃き、そして解決まで至りました。原因は、↓でした。

NSS/3.13.1.0というSSL Versionを使ってるのがマズイようでした。 私のローカルでは、OpenSSLを使うようになっているのに、何故か今回のサーバではNSSを使う設定に。 また、SSLに問題があるのかと思って、

curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE);

みたいな設定しましたが、これでもダメでした。証明書へのパスを渡したりしてもやっぱりダメ。 そもそもリクエストが送信できてない感じなんです。 file_get_contents($uri);だとちゃんとレスポンスが帰るのに…。

ちなみにさくらのVPSでCentOS x86_64、httpd、PHP、mysqlすべてyumで入れたものです。特におかしな設定はしておらず。

NSSとはなんぞや

Mozillaのサイトに詳細がありますね。

NSS FAQ - MDN

引用しますと、

OpenSSL はサーバサイド SSL、TLS、および汎用暗号化機能ライブラリを実装するオープンソースプロジェクトですが、PKCS #11 をサポートしていません。OpenSSL は Eric A. Young と Tim J. Hudson によって開発された SSLeay ライブラリに基づいており、Apache サーバで幅広く使用されています。Apache スタイルのライセンスが付与されています。

NSS は PKCS #11 や S/MIME をはじめとして、サーバおよびクライアント両方のアプリケーションをサポートしています。できるだけ多くの用途を可能にするため、NSS は Mozilla Public License と GNU General Public License の両方でライセンスされています。MPL 条項下または GPL 条項下のどちらでライセンスするかを選択することができます。

とのことです。NSSはサーバサイドだけでなく、クライアントサイドのアプリケーションでもSSLをサポートするような感じでしょうかね。Chromeも使ってるそうです。

Google Chrome が NSS を使うようになったのでSSL/TLS CipherSuite一覧を更新 - 自堕落な技術者の日記

まぁ、それはともかくとして、PHPのcurlでNSSが使われている理由が分かりません・・・。 そして何故かNSSだと、以下の様なcurlのPOST/PUTリクエストのみ失敗するらしいです。

Delayed responses for cURL SSL PUTs/POSTs (php) (NSS vs OpenSSL)

さすがStack Overflowさん・・・。失敗、というよりはレスポンスに遅延が見られる、という内容でしたが、現象はよく似ていて、これがヒントになりました。

ここから解決まであーだこーだやったログなどを書きます。

PHPをソースコードからインストールしてみよう(curlは組み込みだしね)

まずはyumで入れたPHPを削除。

# yum -y remove php php-common

それから、DLとコンパイルします。選択したのはPHP5.3.14です。

# ./configure ... --with-curl --with-openssl ...

って感じで、curlの有効化とOpenSSL使うよーってオプションを渡してコンパイルしました。結果は↓

変わってません。見事にNSS。試しにリクエストしてみましたが、やっぱりダメ。

libcurl.soがおかしいんじゃないの?

libcurlはyumで入れてたんですが、「そもそもこれがおかしいんじゃない?」と気づき、libcurlをソースコードから入れてみる事にしました。cURLのサイトでは、7.26が最新版だったので、これをOpenSSLを有効にしてlibcurl.soを生成してみることにしました。

# wget http://curl.haxx.se/download/curl-7.26.0.tar.gz
# tar xvfz curl-7.26.0.tar.gz
# cd curl-7.26.0
# ./configure --prefix=/usr/local
# make
# make install

特にオプションを指定せずにコンパイルしましたが、OpenSSLを使う設定になっていました。で、できたsoをコピー。

# cp /usr/local/libcurl.so* /usr/lib64/

この状態で、再度PHPをコンパイルし、apacheを再起動した後にphpinfo()を見てみると・・・

OpenSSLになりました!! そして試しにリクエストを送った所、エラーもなく正常にリクエストが成功しました。

と、いうわけで

原因はcurlがSSLにNSSを使っていたから、という原因特定に至りました。 とはいえ、なぜデフォルトの設定でNSSになっていたのか(他に立てたサーバはOpenSSLだったのに)、 そしてNSSだとなぜSSL通信先のPOSTリクエストに失敗するのか。

大事な部分は分からず終いだったわけですが、とりあえず通信ができて、レスポンスが帰ったので良しとします。。。

まとめ

パラメータが云々よりも、コンパイル条件をチェックした方がいい時もありますよ!!

ということですね。