httpsページに接続できない件(wget/curl/HTTP_Request)

つい先日出た現象について覚え書きです。
結論だけ知りたい方向け→「該当箇所をcurlに置き換える事で対応しました」

例の脆弱性の件もあるしやっぱり今後は最新版で動かしたいよね。
って事でためしに以下のAmazonLinuxにてお仕事サイトを動かしてみる事に。

<br>
$ cat /etc/system-release<br>
Amazon Linux AMI release 2014.03</p>
<p>$ openssl version<br>
OpenSSL 1.0.1g-fips 7 Apr 2014</p>
<p>$ rpm -qa | grep openssl<br>
openssl-1.0.1g-1.69.amzn1.x86_64</p>
<p>$ php -v<br>
PHP 5.5.10 (cli) (built: Mar 27 2014 23:56:37)<br>
Copyright (c) 1997-2014 The PHP Group<br>
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies</p>
<p>$ pear version<br>
PEAR Version: 1.9.4<br>
PHP Version: 5.5.10<br>
Zend Engine Version: 2.5.0<br>

ところが別系統システムへの接続で問題が…
内容としてはPEARのHTTP_RequestでAPIをコールというありがちな処理なんですが…
PHP上でAPIのレスポンス自体が取得できない。
(HTTP_Request2じゃない点がアレですがそこは色々な事情があってゴニョゴニョ)

問題を切り分けたいので既存システムとは別にphpファイル作って

<br>
require_once "PEAR.php";<br>
require_once "HTTP/Request.php";<br>
$http_request = new HTTP_Request("https://hoge/api");<br>
$http_response = $http_request-&gt;sendRequest();<br>
if (PEAR::isError($http_response)) {<br>
  echo $http_response-&gt;getMessage();<br>
} else {<br>
  echo $http_request-&gt;getResponseBody();<br>
}<br>

動かして見たところ
<br>
fsockopen(): unable to connect to ssl://hoge/api:443<br>

HTTP_Requestに問題ありか?ってことでwgetで試す

<br>
$ wget https://hoge/api<br>
--2014-04-18 15:17:25--  https://hoge/api<br>
hoge をDNSに問いあわせています... xxx.xxx.xxx.xxx<br>
hoge|xxx.xxx.xxx.xxx|:443 に接続しています... 接続しました。<br>
OpenSSL: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version<br>
SSL による接続が確立できません。<br>

うーむSSLの接続自体に問題があるっぽい。
英語で出てきた一部情報によるとプロトコルのネゴシエーションがどうたらこうたら。
試しにwgetのオプションに
–secure-protocol=TLSv1
をつけてみたら取得成功

ちなみに別のサイトのhttpsページをオプション無しでwgetしてみるとすんなり取得成功。
なんなんだ・・・
普段このあたりを意識する事などないのでなかなか厳しい。

curlでもためしてみるか。
SSLバージョン指定できるオプションがあるようなので

<br>
$ch = curl_init();<br>
curl_setopt_array($ch,<br>
  array(<br>
    CURLOPT_URL=&gt; "https://hoge/api",<br>
    CURLOPT_SSLVERSION=&gt;3,<br>
  )<br>
);<br>
curl_exec($ch);<br>
curl_close($ch);<br>

こちらは取得成功。
※CURLOPT_SSLVERSION無しで動かしてみるとNG。

HTTP_RequestにはSSLバージョン指定的なオプションが無いようなので、試しに直接書き換え
/usr/share/pear/HTTP/Request.php
707行目あたりの

<br>
$host = 'ssl://' . $host;<br>


<br>
$host = 'tls://' . $host;<br>

に変えてみたら取得成功。
やっぱりこのへんか…

HTTP_Request2ではAdapterとしてcURLが指定できるそうなので、そっちに置き換えた上でSSLバージョン指定してしまうのが順当な感じか。
しかしモノがモノだけにさくっと置き換えて完了という訳にもいかず。
ちょっと古めのopensslなら問題無しなんだけどいつまでもそれではちょっと・・・
どうしたもんですかね。

参考サイトにあったようにPHP再コンパイルっていう手もある模様。
phpinfo見てみたらうちも見事にSSL VersionはNSS/3.15.3でした。

参考にさせていただいた事例
ニコニコ動画にhttps(SSL)接続できなかった件
PHPのcurlで”SSL Connection Error.”のエラーがどうしても出る時の対策

追記
参考サイトのはてブコメントにて気になる情報。
相手方が古めのIISだとこうなるという。あ・・・
結局、該当箇所だけcurlに置き換えることでどうにかこうにか対応完了

更に追記(2014/10/16)
SSL3.0が無効になってて困ってる事例多数出ているようです