連載
» 2013年10月21日 18時00分 UPDATE

実録、Androidアプリの脆弱性を撲滅せよ!(後編):「ONETOPIヘッドライン」アプリの脆弱性を修正してみた (1/2)

ソニーデジタルネットワークアプリケーションズ(SDNA)製の脆弱性検査ツール「Secure Coding Checker」を用いてITmediaが開発したAndroidアプリ「ONETOPI ヘッドライン」を実際に検査し、どんな脆弱性が見つかりどう修正したかを「実録」風にお届けします。

[Androidアプリ自宅部,@IT]
onetopi_hl.png ニュースアプリ「ONETOPIヘッドライン」の画面

 「概念として、『安全なアプリ』がいいことは百も承知だが、具体的にどうコードに落とし込んでいけばいいのだろうか」――こんな悩みを抱えているAndroidアプリの開発者は、少なくないのではないでしょうか。この記事では、現実のアプリを例に取って、そのその解決の助けになるツールを紹介していきます。

 登場するのは、ソニーデジタルネットワークアプリケーションズ(SDNA)製の脆弱性検査ツール「Secure Coding Checker」と、ITmediaが開発したAndroidアプリ「ONETOPI ヘッドライン」です。

 前編の「『ONETOPIヘッドライン』アプリの脆弱性を検査してみた」では、Secure Coding Checkerを用いてONETOPI ヘッドラインを実際に検査し、そこで検出された脆弱性の傾向を把握しました。

scc01_scr03.png

そして、

  1. アクセス制御の不備による問題
  2. Log出力による情報漏えいの問題
  3. HTTPS通信の問題

のうち、1つ目のアクセス制御の不備による問題を、Secure Coding Checkerから日本スマートフォンセキュリティ協会(JSSEC)の『Androidアプリのセキュア設計・セキュアコーディングガイド』を参照しながら修正するところまでを紹介しました。

 引き続き、残る2つの問題について、ONETOPIヘッドラインを開発している高間さん(仮名)と、Secure Coding Checkerの開発元であるSDNAが協力して安全なコードに修正していく様子を、実録形式でご紹介します。

Log出力に関する脆弱性の修正

「Log出力に関する脆弱性」とは

 Android 4.0以前のバージョンの端末では、Androidアプリが出力したログデータは、同じ端末内にある他のアプリから読み取れるようになっています。そのため、センシティブな情報の漏えいを防ぐため、「ログ出力しても問題のない情報」と「ログ出力してはならない情報」を分類し、適切にログ出力の実装を行う必要があります。

 セキュアコーディングガイドの4.8.3.2.では、ログレベルとログ出力メソッドの選択基準について記載しており、出力するログ情報の趣旨に添ったメソッドを使い分けるよう定められています。

ログレベル メソッド 出力するログ情報の趣旨 アプリリリース時の注意
ERROR Log.e() アプリが致命的な状況に陥ったときに出力するログ情報 左記のログ情報はユーザーも参照することが想定される情報であるため、開発版アプリとリリース版アプリの両方でログ出力されるべき情報である。そのため、このログレベルではセンシティブな情報をログ出力してはならない
WARN Log.w() アプリが深刻な予期せぬ状況に遭遇したときに出力するログ情報
INFO Log.i() 上記以外で、アプリの注目すべき状態の変化や結果を知らせる目的で出力するログ情報
DEBUG Log.d() アプリ開発時に特定バグの原因究明のために一時的にログ出力したいプログラム内部の状態情報 ログ出力されてはならない情報である。開発版アプリではセンシティブな情報を出力しても構わないが、リリース版アプリでは絶対にセンシティブな情報をログ出力してはならない
VERBOSE Log.v() 以上のいずれにも該当しないログ情報。アプリ開発者がさまざまな目的で出力するログ情報が該当する。サーバとの通信データをダンプ出力したい場合など
表5 ログレベルとログ出力メソッドの選択基準(セキュアコーディングガイド4.8.3.1.から引用)

 リリース版のアプリで、誤ってセンシティブな情報をログ出力してしまうと、他のアプリから読み取られてしまう可能性があり、情報漏えいにつながります。過去の例を見ると、JVN#23328321として報告されている問題などが、センシティブな情報をログ出力してしまったことが原因で発生しています。

Secure Coding Checkerの結果確認

 ONETOPIヘッドラインでは、Log#d()/v()メソッドの利用が検出されており、メソッドを呼び出している個所が表示されていました。表5にあるとおり、Log#d()/v()メソッドは、デバッグ版アプリでのみ利用すべきメソッドですから、リリース版アプリで利用している個所がある時点で「違反」になります。

 他に、Log#e()/i()/w()とSystem.out/errについても同様の問題が検出され、質問が表示されていました。

scc01_scr06.png 画面6 ログ出力の検査結果画面

高間:ONETOPIヘッドラインではログ出力は行っていないつもりだったのですが、問題が検出されているのはなぜでしょうか?

SDNA:Log.d()/v()を呼び出している個所を確認すると、ONETOPIヘッドラインのパッケージ名とは異なる個所での検出でした。おそらく、ONETOPIヘッドラインが利用している外部モジュールが、Log.d()/v()を使ってログ出力しているのだと思われます。Secure Coding Checkerは外部モジュールも検査対象としていますので、今回問題が見つかったわけです。

 利用者の立場からすると、ONETOPIヘッドライン自体が原因であろうと、ONETOPIヘッドラインが利用する外部モジュールが原因であろうと、脆弱性によって被害を受けてしまえば問題であることに変わりはありません。自分が利用する外部モジュールについても、セキュリティ上の問題がないか確認することが必要です。

高間:分かりました。あと、System.out/errの質問に回答したら「違反」になってしまったのですが、これはどういうことでしょう?

scc01_scr07.png 画面7 System.out/err検査項目

SDNA:Androidアプリの場合、System.out/errの出力先はLogCatになります。明示的にSystem.out/errを利用したときはもちろんですが、アプリで発生した例外をCatchしなかった場合など、暗黙的にSystem.out/errにデータ出力される場合もあります。このような場合でも、センシティブな情報の漏えいを防ぐためにSystem.out/errのログ出力の抑制をする必要がある点に関する指摘になりますね。

コードの修正

 ログ出力の検出結果を確認し、高間さんはまずLog.d()/v()の対応に取り掛かりました。

 セキュアコーディングガイド 4.8.2.2.を確認すると、ProGuardを利用することで、ログ出力するメソッドを削除する方法が記載されていました。今回のアプリではログ出力は不要と考えていましたので、Logクラスの関数をすべて削除するようにProGuardを設定しました。

修正前 ファイル:なし
    -assumenosideeffects class android.util.Log { public *; }
修正後 ファイル:proguard-project.txt

 次に、System.out/errですが、セキュアコーディングガイド4.8.3.7.に記載されているSystem.out/errの出力先を変更する方法を取り入れました。

<application 
    android:label="@string/app_name"
    android:icon="@drawable/ic_launcher"
    android:theme="@style/Theme.Prism"
>
修正前のAndroidManifest.xml
<application 
    android:label="@string/app_name"
    android:icon="@drawable/ic_launcher"
    android:theme="@style/Theme.Prism"
    android:name=".OutputRedirectApplication"
>
修正後のAndroidManifest.xml
修正前 ファイル:なし
public class OutputRedirectApplication extends Application {
    private final PrintStream emptyStream = new PrintStream(new OutputStream() {
        public void write(int oneByte) throws IOException {
        }
    });
    @Override
    public void onCreate() {
        PrintStream savedOut = System.out;
        PrintStream savedErr = System.err;
        System.setOut(emptyStream);
        System.setErr(emptyStream);
        resetStreams(savedOut, savedErr);
        registerActivityLifecycleCallbacks(this);
    }
    private void resetStreams(PrintStream savedOut, PrintStream savedErr) {
        System.setOut(savedOut);
        System.setErr(savedErr);
    }
修正後のOutputRedirectApplication.java

高間:問題の修正方法は、Secure Coding Checkerの指摘個所に表示されているリンクボタンから、セキュアコーディングガイドの該当個所に飛んでくれたので、迷うことがありませんでした。ガイドに書いてあるソースコードも、コピペしたらそのまま動いてくれたので、修正に時間がかからなかったのは非常に助かりました。

 Log.d()/v()とSystem.out/errの問題を修正すると、「違反」と判定されていた項目はすべて「安全」となり、ログ出力の脆弱性が解消されていることが確認できました。

【コラム】外部モジュールのセキュリティ対応

 Androidアプリの開発では、開発効率や機能拡張・収益源の確保などの理由から外部モジュールを利用するケースが珍しくありません。

 しかし、中にはセキュリティを考慮していない脆弱な外部モジュールがあり、またアプリ開発者の多くは外部モジュールの脆弱性を気にしていません。もし利用する外部モジュールに脆弱性が見つかった場合、アプリ開発者がその問題を修正することは非常に難しく、ほとんどの場合、そのまま利用することになります。今回のケースのようにProGuardを使った方法で修正できたのは、非常にまれなケースです。

 どうしても外部モジュールを利用する必要がある場合は、外部モジュールに起因する想定被害を洗い出し、発生可能性なども検討してセキュリティ上のリスクを明らかにしておくことが重要です。リスクを明らかにした上で、外部モジュールを利用すべきか、利用しても問題ないかを判断します。セキュリティ上のリスクが大きい場合は、外部モジュールの利用を止めて代替手段を用意することも必要です。


       1|2 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。