サーバとクライアントとの間で安全にデータをやり取りするために、HTTPS通信によって通信を暗号化する方法があります。ただ、安全に通信するために使っているはずのHTTPS通信でも実装方法に誤りがあると、暗号通信を第三者に盗聴されてしまう可能性があります。
実際、HTTPS通信においてサーバ証明書の検証に不備があったため、中間者攻撃と呼ばれる攻撃方法によって、第三者に暗号通信を盗聴される可能性があったAndroidアプリの事例が報告されています。JVN75084836などがその一例です。
HTTPS通信のサーバ証明書の検証方法に欠陥が入り込んでしまう背景の1つが、いわゆる「オレオレ証明書」です。信頼できる第三者認証局から発行されたサーバ証明書ではなく、私的に発行したサーバ証明書を使って運用テストなどを行うため、サーバ証明書の検証を意図的に省いた状態でHTTPS通信してしまう実装が原因として挙げられます。
ONETOPIヘッドラインでは、安全でない独自のX.509 TrustManager、安全でないHostnameVerifier、SSLSocketFactory - ALLOW_ALL_HOSTNAME_VERIFIERが「HTTPSで通信する」項目で、安全でないWebClientView#onReceivedSslError()が「WebViewを使う」の項目で、それぞれ「違反」として検出されました。これらはすべて、サーバ証明書の検証不備の問題に起因するものです。
高間:サーバ証明書の検証方法を省略する方法は意図的に実装したものだったので、この検出結果はすぐに納得できました。開発期間中は独自に発行したサーバ証明書でテストしたかったため、サーバ証明書の検証を省いて実装していました。
SDNA:多くの場合は高間さんと同じように、独自に発行したサーバ証明書をテスト用途で使うために、サーバ証明書の検証を省略しているようです。リリース段階で、実装したコードを取り除けば安全になりますが、誤ってそのままリリースしてしまうかもしれませんので、良い方法とはいえません。
一番楽で安全な方法は、テスト期間中であっても、信頼のおける第三者認証局からサーバ証明書を購入し、運用することです。テスト用途として限ってしまえば、今では安価・短時間にサーバ証明書を調達できます(http://www.ssl.ph/など)。テスト用のサーバ証明書は、他のアプリ開発にも使い回すこともでき、お勧めしている方法です。
他に、プライベート証明書でHTTPS通信する実装・運用の方法がセキュアコーディングガイドに記載されていますので、こちらも確認してください(セキュアコーディングガイド 5.4.1.3.)。
HTTPS通信の問題を修正するためのアプローチにはいくつか選択肢があります。今回のケースでは、残念ながらサーバ証明書を購入する方法は採用できなかったため、BuildConfigを利用してデバッグ時のみにサーバ証明書の検証を省略することにしました。
(省略) TrustManager trustManager = new X509TrustManager() { (省略) public X509Certificate[] getAcceptedIssuers() { return null; } }; (省略)
(省略) if (BuildConfig.DEBUG) { (省略) TrustManager trustManager = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } }; } (省略)
同様にWebViewでも、BuildConfigを利用して、デバッグ時のみサーバ証明書検証を省略する方法を採用しました。
WebViewClient mWebViewClient = new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } (省略)
WebViewClient mWebViewClient = new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { if (BuildConfig.DEBUG) { handler.proceed(); } else { handler.cancel(); } } (省略)
高間:今回はBuildConfigを使って修正しましたが、この方法で問題なかったでしょうか?
SDNA:今回のケースでは、BuildConfigを使った修正方法でもセキュリティ上問題はありません。ただし、この修正方法ですと、動作検証の際に動作しているアプリがリリース版なのかデバッグ版なのか正しく把握しておく必要があり、十分注意が必要です。また、BuildConfig はADTのバージョンによって正しく動作しないケースもありますので、この点でも注意が必要です。可能であればサーバ証明書を調達する方法が望ましいので、今後の開発での採用を検討してみてください。
この修正によってHTTPS通信の項目で「違反」と判定されていた項目がなくなり、安全になりました。
Secure Coding Checkerには、ユーザー判断と呼ばれる機能があります。これは、Secure Coding Checkerが「違反」「注意」「質問」と検出した結果に対して、ユーザー自身の判断で結果を変更する機能です。
結果を変更する理由は2種類あります。1つはSecure Coding Checkerの判断基準(セキュアコーディングガイド記載のセキュアコーディング方法)と異なる別の方法で安全を確保・確認している場合の「安全判断」、もう1つは安全だとはいえないが、リスクを評価し、軽微な問題であると判断して、コードは修正しないとする場合の「リスク受容」です。
ユーザー判断により検査結果を変更する場合は、判断理由に問題がなかったかを後から確認できるようにするため、変更理由を記録として残しておくことが必要です。Secure Coding Checker上で結果を変更する際には、一緒に変更理由も記載できますので、必ず記録を残すようにしてください。ユーザー判断で結果を変更した項目とその変更理由は、検査結果サマリ画面で確認できます。
今回確認した項目以外にも、すべての項目について、結果確認・コード修正した結果、ONETOPIヘッドラインから「違反」「安全」「質問」項目がなくなり、すべて安全となりました。この結果、ONETOPIヘッドラインはセキュアコーディングガイドに沿った安全なアプリであることになります。
最後に、今回のONETOPIヘッドラインの脆弱性を修正していく活動の感想を、開発者の高間さんに聞いてみたところ……。
高間:Androidアプリのセキュリティはずっと気になっていたのですが、なかなか取り組みを始めることができませんでした。その意味で、今回の取り組みはとても勉強になりました。
SDNA:それは良かったです。ただ、Androidアプリのセキュリティ対策は、新しい問題が見つかったり、Android自体がバージョンアップされるなどで、日々変わっていきます。それに合わせて、セキュアコーディングガイドも新しい内容を盛り込む形で進化していきますので、今後もセキュアコーディングの学習を続けていってください。
高間:分かりました。継続的な学習が大切なのですね。セキュアコーディングガイドとSecure Coding Checkerを組み合わせて使うと、簡単に脆弱性を発見・修正し、セキュアコーディングを学ぶことができると分かりました。今後の開発でもぜひ利用したいと思います。
このようなわけで、今回取り上げたONETOPIヘッドラインは脆弱性を修正した状態で、Google Playにて公開中です。ぜひ安心してご利用ください。
Copyright © ITmedia, Inc. All Rights Reserved.