読者です 読者をやめる 読者になる 読者になる

カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

SSL/TLS通信できないときのハマりポイント9選

トラブルシューティング トラブルシューティング-SSL

サービスをSSL/TLS化する上で、簡単にうまくいくこともあれば、何か設定が抜けていたり、間違っていたりして、どうもうまくいかないことがあります。そういうときに、僕が確認しているポイントを書いてみたいと思います。後半の2つは少しマニアックな感じなのですが、ハマる可能性もあるので、よかったらご参考ください。これから新卒でエンジニアになる方は前半の7つをちゃんと身につけておきましょう。

1. telnetで通信できるか確認しよう

まずそもそも通信できているか確認しましょう。「https://example.org」にアクセスしたいのであれば、以下のように叩きましょう。

$ telnet example.org 443
Trying 111.111.111.111...
Connected to example.org.
Escape character is '^]'.

111.111.111.111というIPは適当ですが、こんな感じで表示されたら通信できてます。すぐ切断したり「Escape character」が出てこないと通信できてないので、設定がおかしいでしょう。

2. netstatでポートがリッスンしているかの確認してみよう

これも基礎中の基礎ではありますが、以下のような感じでnetstatで見てみます。

$ netstat -tnl

これはSSL/TLSの処理をするリアルサーバで叩きます。まず必要なポートがリッスンしているか確認しましょう。443を処理するのであれば、443番ポートが立ち上がっているはずですし、SSLアクセラレータを挟むのであれば、80番などアプリが立ち上がっているでしょう。

3. curlでレスポンスが返ってくるか確認しよう

telnetもできて、netstatでポートも立ち上がってることが確認できた。 そうしたら、今度はcurlしてみましょう。

$ curl examle.org
# またはリアルサーバ上で以下。
$ curl -H 'Host: pigg.ameba.jp' http://localhost

Hostヘッダを付与することで、グローバル経由ではなくローカル経由でテストすることができます。 レスポンス返ってくれば問題ないでしょう。

4. ヴァーチャルホストの設定を確認しよう

curlの例で確認方法は書いてしまいましたが、複数のバーチャルホストを切っている場合に、正しいホストのほうに接続できない場合があります。そのため、Hostヘッダを使って、リアルサーバ上でテストしておくといいですね。

5. クロスルート

ここまでは、サーバ上の設定を見てきましたが、今度は証明書関連の問題ですね。古いガラケーやAndroid端末などの場合にアクセスできないような状態があると、クロスルート証明書を使ってない場合があります。 ぱっと見情報が見つかったのは以下。サイバートラスト社の携帯・スマートフォン対応。こういうところに対応端末が書かれているので、見ておくとよいですね。他の会社はどうなのだろう。

6. 2048bit

1024bitの証明書を使っていて、2048bitに変えた時に、同じように古い端末がやはり使えなくなりました。なので、古い端末でアクセス出来ない場合は2048bitをサポートしてないか確認しといたほうがいいですね。

7. SHA1 SHA2

最近はこの話題が多いですかね。SHA1使っているところも多いと思いますが、これからの時期に発行して、有効期限が2016年以降だと、各ブラウザがどんどん警告を出すようにしているので、意識しといたほうがいいですね。ブラウザ等のクライアントでアクセスはできますが、地味に警告が出ます。

たとえば、SHA1で作った証明書で、かつ有効期限が2016年以降のものをChrome39の最新版で開くと以下のような警告がでます。

f:id:masudaK:20160330131839p:plain

ブラウザによって、警告出るものもあれば出ないものもありますし、有効期限によって違いもあったりします。そのため、2015年が有効期限のものはChromeでも警告出ずという感じなので、すごく分かりづらいです。

詳細はこの記事に書かれているので読んでいただくのがいいかと思います。

また、これらを回避するためにSHA2にすると、Android2.2等古い端末でアクセスできなかったりして、2048bitのときと同じような問題が起こります。そろそろSHA1の問題からも逃げられない時期になってきましたので、把握しといたほうがよいでしょう。

8. OCSPの確認

これはFirefoxでハマる内容かと思いますが、FirefoxではデフォルトでOCSPが有効になっています。OSCPというのは証明書の失効状態を確認するためのプロトコルで、この確認があると初回接続に時間がかかるので、デフォルト無効になっているブラウザもあるのですが、Firefoxでは有効になっています。 これがどういう問題を生むかというと、証明書発行してから数時間内にアクセスした場合に、そのドメインにアクセス出来ない問題が発生することがあります。「OCSP server has no status for the certificate」というエラーが出た場合はこの問題を疑いましょう。

あらゆるCAで発生するかどうかまでは確認できていないのですが、会社によってはOCSPサーバのデータと、発行された証明書の同期が取れるまでに2時間近くかかったりするところがあります。もっと長いところもあるかもしれない。

そうすると、証明書が発行されてからすぐそのドメインにアクセスすると、上記のエラーが出てしまいます。一度出てしまうと結果をキャッシュしてしまうため、更に数時間アクセスできなくなってしまいます。

地味にハマったりするので、把握しといたほうがいいかもですね。

9. オレオレ

2015/01/29追記。オレオレ証明書と通信できるようにクライアント側で実装するのは、リスクも踏まえないといけないので、その点は誤解ないようお願いします。
デバッグビルドにだけ有効化したり、警告を出した上で許可したりしないと、証明書の意味がなくなってしまうので、気をつけてください。

オレオレ証明書にアクセスしたら警告が出るというのは特に改めて理解することもないでしょう。署名がオレオレなので、警告は致し方ない。

ただし、WindowsクライアントやスマホアプリなどLLでは作られていないものを使って、オレオレにアクセスするとわりとハマります。RubyとかのHTTPクライアントではハマったことないのですが、Visual C++とかだと見事にハマります。

どういうことかというと、「INTERNET_FLAG_IGNORE_CERT_CN_INVALID」や「INTERNET_FLAG_IGNORE_CERT_DATE_INVALID」といったフラグがついてあれば、問題なく通信できると思っていました。

これらはどういう意味があるかというと、以下のように書かれています。

- INTERNET_FLAG_IGNORE_CERT_CN_INVALID - 要求には、ホスト名と一致しないサーバーの証明書のホスト名に起因する可能性があるエラーは無視されます。
- INTERNET_FLAG_IGNORE_CERT_DATE_INVALID では、サーバーの有効期限が切れた証明書によって発生したエラーを無視します。
http://support2.microsoft.com/kb/168151/ja参照

なので、以下のようにすれば問題なくリクエスト飛ばせると思ったのですね。

dwFlags |= INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;

HINTERNET hRequest = HttpOpenRequest(hConnection, _T("POST"), lpwcUploadPath, HTTP_VERSION, NULL, NULL, dwFlags, NULL);

ただ、これだとエラーになってしまいます。その場合には以下のようにしなければなりません。

DWORD dwError = GetLastError();
if (dwError == ERROR_INTERNET_INVALID_CA)
{
    DWORD dwFlags;
    DWORD dwBuffLen = sizeof(dwFlags);

    InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS,
        (LPVOID)&dwFlags, &dwBuffLen);

    dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
    InternetSetOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS,
        &dwFlags, sizeof(dwFlags));
    goto again;
}

GetLastError()で「ERROR_INTERNET_INVALID_CA」を返した場合は、オレオレ証明書対策ということで「SECURITY_FLAG_IGNORE_UNKNOWN_CA」もセットしておきましょう。

これで問題なく通信できるようになるかと思います。

終わりに

単にSSL/TLS通信のデバッグといっても、サーバサイドなのかクライアントサイドなのか、思ってる以上に問題は厄介だったりします。過去の経験を振り返っても、「普通に」設定する分には設定箇所はそこまで多くないのでそこまでハマるところないのですが、今回の記事の後半のような少し厄介な箇所で問題があった場合は、いきなり難易度があがり、問題が複雑化します。

またTwitter社のPerfect Forward Secrecy化に見られるような、楕円曲線暗号使ったりするような流れや、サービスをすべてSSL/TLS化したりするような流れが近年よく見られ、2,3年前に比べて、技術的に理解しないといけないことが増えている気がします。

それぐらいSSLは奥深い技術であるでしょうし、勉強も色々しないといけないと思いますが、そういった中で少しでも参考になればよいと思い、僕が以前ハマった内容を書いてみました。

何かあれば、@masudaKまでご連絡頂ければと思います!