AndroidにおけるSNI対応状況

はじめまして。株式会社ガラパゴスの細羽と申します。このたび、さくらのナレッジに寄稿させていただく事になりました。よろしくお願いいたします。

弊社は、スマートフォンアプリの受託開発を主な事業としており、アプリ単体の新規開発のみならず、以下のようなトータルなサービス提供を強みにしております。

  • モバイル戦略の立案:ビジネスモデル分析・KPI・KGI策定・運用設計など
  • サービス全体の開発:サーバサイドまで含めたサービス全体の設計・開発
  • 既存サービスのモバイル対応:お客様の既存資産(WEBサービスやDB)を活用してモバイル対応させるための開発プラン提案・実行

このような領域で開発業務に取り組んでいると、新規サービスの開発ではあまり直面しない、一風変わった技術的な課題に直面することも少なくありません。この中でも特に、サーバやインフラに関するナレッジを今後共有していけたらと思います。

テーマ

本日のテーマは「AndroidのSNI対応状況」です。AndroidにおけるSNIの対応状況を調査する機会があったのですが、非対応時の挙動確認まで含めた情報が意外と少なかったのでテーマにしてみました。

SNIとは

SNI(Server Name Indication)とは、RFC6066内で定義されている、SSL/TLSの拡張仕様の一つです。一言で言えば、「ホスト名によって異なる証明書を使い分けることができる」という拡張仕様になります。

本来のSSL/TLSの仕様の下で異なる証明書を使い分けるためには、IPアドレス・ポート番号の組み合わせを変える必要があります。ApacheのVirtualHostを例にすると、複数のグローバルIPアドレスを付与した上で、IPベースのVirtualHostで運用する必要があります。名前ベースのVirtualHostでは、証明書の使い分けは出来ません。

これがSNIの導入により、名前ベースのVirtualHostであっても証明書の使い分けが出来るようになりました。

SNIの仕様イメージ

SNIはプロトコルの拡張なので、サーバはもちろんのこと、クライアント側も対応している必要があります。幸いにして、SNI自体は決して新しいものではなく、PCブラウザに限れば、昨今の主要なWEBブラウザではほぼ対応されています。

ちなみに、SNIに対応していないクライアントがサーバにHTTPSアクセスした場合にどうなるのでしょうか?サーバの設定にも依存しますが、例えばApacheの標準的な設定では「デフォルトVirtualHostの証明書を返す」という挙動となります。

つまり、デフォルトVirtualHostへのHTTPSアクセスであれば結果的に問題は無いのですが、非デフォルトVirtualHostへのHTTPSアクセスの場合は、いわゆる「証明書名不一致エラー」の状態になります。

# Apacheでは、この設定でSNI未対応のデフォルトVirtualHostの証明書を返すようになる
SSLStrictSNIVHostCheck Off

SNI非対応クライアントの証明書名不一致エラー

AndroidのSNI対応状況

それでは、Androidの対応状況はどうなっているのでしょうか?

モバイル対応のWEBサービスを展開するにあたり、HTTPSアクセスが発生し得る箇所をプログラムの内部構造の視点で列挙して、個別に対応状況を見ていきます。

  • 標準WEBブラウザ
  • ネイティブアプリ
    • HTTPクライアント
    • WebView

標準WEBブラウザ

Android 3.0 (Honeycomb)以降の標準ブラウザであれば、SNIに対応しています。ただし、Androidでは標準ブラウザ以外にも様々なブラウザが存在するため、それらの対応は別途考慮する必要があります。

例えばAndroid2.3のようにSNI非対応のブラウザで、非デフォルトVirtualHostにHTTPSアクセスすると、下のような証明書名不一致エラーのポップアップが表示されます。

標準WEBブラウザの証明書名不一致エラー

ネイティブアプリ:HTTPクライアント

次に、HTTPクライアントの対応状況です。Androidのネイティブアプリでは、WebAPIを呼び出したり、ImageViewに設定する画像をWEB経由で取得する場合など、多くの場面でHTTPクライアントが利用されます。

主なHTTPクライアントの対応状況は次の通りです。

  • Apache HttpClient
    • SNIには未対応
  • HttpURLConnection
    • 2.3以降でSNIに対応 (HttpsURLConnectionクラスを利用する)

SNI非対応のHTTPクライアントで、非デフォルトVirtualHostにHTTPSアクセスした場合は、大抵のクライアントであれば例外(Exception)がthrowされます。

他にもVolleyやOkHttp等のクライアントもあるので、今後追加で調査してみたいと思います。

ネイティブアプリ:WebView

最後に、WebViewの対応状況です。数台の端末でサンプルプログラムを動かして確認したところ、標準ブラウザの対応状況とほぼ同じく、Android2.3ではNG、Android4.0以上ではOKという結果になりました。

SNI非対応のWebViewで、非デフォルトVirtualHostにHTTPSアクセスすると、以下のようにWebView画面が真っ白になります。

SNI非対応WebViewの画面

標準WEBブラウザのようにセキュリティ警告のポップアップが表示されても良さそうなものですが、WebView自体はHTMLビューアに特化した非常にシンプルなコンポーネントであるため、この種のエラーハンドリングは自前で実装してあげる必要があります。

// WebViewでSSLエラーを処理する実装例
WebView webView = (WebView) findViewById(R.id.webview);
webView.setWebViewClient(new WebViewClient(){
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        // ここでSSLエラーを適切に処理してあげる
        
        // ここでhandler.proceed()すれば処理は進められるが、脆弱性の元になるのでダメ・ゼッタイ
        // handler.proceed();
    }
});

最後に

自社でイチからサービスを開発するような場合は、インフラ設計にある程度制御が効く場合が多く、本テーマに関連する課題に直面するケースは稀かもしれません。

しかし今後、スマートフォンの利用拡大に伴い、既存システムとの結合や移行を伴う開発も増えてきて、何かとインフラに関連したハマりどころも増えてくる事が予想されます。

「WebView内で表示していた外部ドメインの画像が、実はSNIで動いているサーバのURLだった」とか「呼び出していたサードパーティのWebAPIが、実はPaaSのSNIプランSSLで動いていた」といったことも起こらないとも限りません。

最後までお読み頂き、ありがとうございました。

おしらせ

banner_cloud