皆さま、初めまして。Yahoo! JAPANでAndroidアプリを開発している筒井 俊祐です。
私はAndroid OSバージョンが1.6の頃よりYahoo! JAPANでAndroidアプリ開発に携わっています。その頃は今ほど情報がなく「どうやってAndroidアプリを作るのか」を試行錯誤しながら開発していました。セキュリティについても同様にベストプラクティスがまとまっておらず、セキュリティチェックの仕方も開発者によってまちまちだったのかなと思います。
特に「WebView」を利用したアプリは脆弱性を生むケースが多発していました。fileスキーマ(file:// で始まるリンク)を除いておかないと、アプリ内に保存しているファイルの読み取りができたり、4.2未満では「WebView#addJavascriptInterface()」のメソッドを利用すると、リフレクションを経由することで任意のメソッドが実行できるという脆弱性が出たりで、Androidのセキュリティに不安を感じる状況もあったと思います。
しかしここのところ、Androidアプリ開発者のセキュリティを取り巻く環境は大きく改善されており、開発者向けセキュリティ対策のドキュメントも徐々に拡充されています。日本スマートフォンセキュリティ協会が公開している「Androidアプリのセキュア設計・セキュアコーディングガイド」や、Android開発者向け公式サイトにもセキュリティ対策専用ページが用意されるなど、セキュリティのベストプラクティスがまとまってきています。
もちろん、Android OSバージョンの違いによって脆弱性の有無があるなど、開発者が主体的に意識しなければならないポイントは多く存在します。その一方で「Androidのセキュリティ」と一言で言っても、学ぶべき技術の幅は広く、1人の人間でそれらを網羅することは容易ではありません。私たちも「セキュリティは大変だなぁ」と悩みながら学んでいます。
この連載では、いくつかのポイントに絞り、私たちYahoo! JAPANで働くAndroidアプリエンジニアが意識しているセキュリティのポイントを説明します。この連載がみなさんのAndroidアプリのセキュリティを高めるきっかけになれば幸いです。
Androidアプリのセキュリティ対策のキホン
皆さんは「Androidアプリのセキュリティ対策」でどういうポイントを意識していますか? 例えばマネージャーに「担当しているアプリのセキュリティ対策を教えて欲しい」と聞かれたらどのように答えますか?
Yahoo! JAPANではセキュリティのチェックリストを用意し、これに沿って開発を進めています。リストに準拠する形でセキュリティ対策を行えば、チェック項目と内容の整合性を確認すればいいので、マネージャーも安心できますよね。
今回は、「『ToDoリスト』アプリを開発しているプロジェクトチームからセキュリティのチェックを依頼された」という想定でチェックしてみましょう。今回のケースでは以下のような機能を持っているアプリと仮定します。
- 共有(ACTION_SENDのアクション)を通じて、他アプリからToDoリストを追加
- アプリのプライベートのデータ領域にToDoリストのデータを保存
- ToDoリストの項目の追加・削除
- ヘルプページを用意する
- サーバーとToDoリストの同期
想定しているToDoリストのアプリ |
それでは、以下の6つのセキュリティのチェックリストを一緒に確認していきましょう。
- 外部とのアクセスの確認
- データの適切な保存とその保護の確認
- WebViewの利用方法の確認
- Lintのセキュリティ警告の確認
- 利用しているライブラリの脆弱性を確認
- ログ出力の確認
今回はToDoリストのアプリを想定していますが、もちろんほかのアプリにも応用できると思います。
1. 外部とのアクセスの確認
攻撃者が外部アクセスで狙うポイントは2つ考えられます。
- 外部が公開している機能へアクセスする点
- 外部へ公開している機能にアクセスされる点
ここで「外部」と呼称するものは、自アプリ以外のコンポーネントや、サーバーなどの外部環境を指します。例えば、他アプリの「Activity」や「ContentProvider」といったコンポーネント、自社や他社を問わず外部サーバーが提供しているAPIなどです。
外部に公開している機能を知る
最初に確認すべきことは「何を外部に公開しているのか」を知ることです。意図せずに公開しているコンポーネントがないか、「AndroidManifest.xml」を参照して確認しましょう。以下の2点をチェックすれば、外部に公開しているコンポーネントがわかります。
- exported=”true”となっているもの
- intent-filterを設定しているもの
意図せずに公開しているものがあれば「exported=”false”」を設定して非公開にしましょう。なお「ContentProvider」の場合は、もともと外部へデータを提供する目的のコンポーネントですから、「exported」はデフォルトで「true」です。要は「exported」を指定しないと、外部公開されるということです。
ただ、「exported=”false”」を指定し忘れることによる意図しない公開があり、その結果として脆弱性が生まれるケースもあります。そのため、「targetSdkVersion」が17以上の場合はデフォルトが「false」になるようにプラットフォーム側で修正されました。
また、今回のToDoリストアプリでは「ACTION_SEND」のアクションに対応しています。Androidでは「共有」ボタンで別アプリへ情報共有する場合にこのアクションが利用されます。別のアプリから共有されて受け取ったテキストを解析し、何かしら処理を行う場合は入力値の検証をあわせて行いましょう。
ちなみに補足ですが、例えば「https://」から始まる文字列があるとします。これをタップしてブラウザでリンクを開ける場合、ユーザーの誤タップで意図せずにリンクをブラウザで開いてしまうこともある可能性があります。それを防ぐために同意をとるためのダイアログを表示して、ユーザーの同意がとれてからアクションするようにしても良いでしょう。
「Service」や「BroadcastReceiver」も同様に外部コンポーネントから情報を受け取れますから、必要に応じて入力値の検証を行い、不正値であればエラーとして処理しましょう。
外部へアクセスしている機能を知る
今回のアプリでは、サービスにログインすることでアプリ内に保存しているToDoリストをサーバーに保存できます。通信についてはセキュリティ検証を必要とするポイントがあるものの、これは次回以降の記事で説明します。
なお、認証についてはかなり専門的な知識が必要なため、本記事では説明しません。なお、Yahoo! JAPANでは認証のためのライブラリが用意されており、Yahoo! JAPANでログインする際にはそのライブラリを利用しています。一般の開発者でYahoo! ID連携を行いたい方はYahoo! ID連携を利用してみてください。
2. データの適切な保存とその保護
データの機密性に応じて、保存先と保護を考えましょう。今回のアプリでは、ToDoをほかのアプリから参照されないように、アプリのプライベートなデータ領域に保存します。
アプリで扱っているデータを確認する
初めに「自分たちのアプリでどういうデータを扱っているか」を知ることが重要です。もちろん、イチから自分たちが開発し、アプリで扱っているデータ構造を把握している場合は問題ありません。
しかし、プロジェクトをほかの人から引き継ぐようなケースも考えられますし、そうした場合にはデータ構造を把握できていないこともあるかと思いますので、きちんと確認・把握しましょう。特に、どういうデータを扱っているかがまとまっていないケースでは、どのデータを守れば良いのかが明確になりません。その正確な情報がないことは、間接的にアプリの不具合へと繋がりうるのです。
ちなみに、アプリで取り扱うデータを一律で守る必要があるとは考えていません。例えば、単純な設定情報といった重要でないデータは、コストをかけて守らなくても良いでしょう。逆にデータの機密性が高いものについては、暗号化して守ることが必須です。
アプリのプライベートなデータ領域に保存されているデータは、ほかのアプリからはアクセスできず、基本的に安全です。ただそれでも、100%の安全性が担保されているわけではありません。何かのきっかけ、例えば特定条件下に起こるOSの脆弱性などがありますから、プライベート領域であっても漏えいの可能性は存在するのです。だからこそ、暗号化が必要となります。
通常、「SQLite」や「SharedPreferences」にデータを保存しているかと思いますが、機密性の高いデータは「SQLCipher」を利用してデータベース全体を暗号化したり、SharedPreferencesへの保存では、保存する値を暗号化しましょう。実際に暗号化されているかどうか確認するには、エミュレータ上でアプリを実行して「adb pull」で該当データをパソコンに持ってくる方法が手っ取り早いでしょう。
SQLCipherについて、初めて目にする方もいると思うので補足します。SQLCipherはSQLiteのデータベースに「AES-256」で暗号化できる機能を付加したものです。暗号化によるオーバーヘッドも5~15%と、あまり大きくありません。なお、SQLCipherは最初にNDKのライブラリのロードなどが必要ですが、それ以外は通常のSQLiteと同じように扱えるのでとても簡単です。
ほかの可能性として、機密性が非常に高いデータを扱う場合は、そもそも「データを永続的に保存しない」という選択も大事です。また、データ暗号化にSQLCipherを利用しない場合は、AndoridフレームワークのCipherクラスを利用して暗号化することが、取り扱いも容易で一般的です。ただし、暗号化や暗号化に必要な鍵の取り扱いについては専門的な知識が必要となりますから、セキュリティ専門家に相談することをおすすめします。
SQL Injection対策の確認
Androidアプリは内部でSQLiteを利用できるため、SQLを利用します。外部から取得したデータをパラメータとして利用する場合は、SQL Injection対策のためにきちんとエスケープ処理を行いましょう。
Andoidのフレームワークにある「SQLiteDatabase」でSQLを発行する際も、第1引数で指定したSQLに「?」のパラメータを入れておけば、第2引数で指定する「selectionArgs」の配列の値を一個ずつエスケープしてくれるため、通常通りに開発すればSQL Injectionの対策になるはずです。
-
例 : SQLiteDatabase#rawQueryメソッド
public Cursor rawQuery(String sql, String[] selectionArgs)
3. WebViewの利用方法の確認
アプリのヘルプページについて、自社で管理する外部サーバーでホストされたHTMLをWebViewで表示するケースを考えてみます。WebViewを利用する場合は、以下の4つのポイントをチェックしましょう。
- 信頼できるドメインのみアクセス可能になっていること。
- 4.2未満ではaddJavascriptInterface()の利用をしないこと
- 必要ない限りJavascriptを利用しない。setJavaScriptEnabled(false)としておくこと
- Webviewで利用できるプロトコルをhttps:// に制限すること。(file:// などはロードできないようにしておく)
なお、単純なWebページを表示する場合はWebViewを利用せず、「Chrome Custom Tabs」の利用を検討してみてください。今回のケースはこの機能を活用する例に該当すると思います。Chrome Custom TabはChromeのバージョン45以上をインストール済み、かつ4.1(Jelly Bean)以上であれば利用可能です。Chrome Custom Tabを利用すれば、4.4以下のOSバージョンでもChromeで実装されている最新のセキュリティの実装が利用できたり、コンテンツのプリフェッチによってパフォーマンス向上も期待できるという、WebViewでは得られないメリットがあります。
第2回の後編では「4.Lintのセキュリティ警告の確認」より解説します。
著者紹介
筒井 俊祐(つつい しゅんすけ)
ヤフー パーソナルサービスカンパニー アプリ開発部 部長
2005年ヤフー株式会社入社。2009年よりAndroidアプリ開発に従事し「ヤフオク!」、「Yahoo!ブラウザー」、「Yahoo!かんたんバックアップ」など多数のアプリ開発業務を行ってきた。
2013年Androidの黒帯に認定され、社内エンジニアの技術力強化にも携わっている。著作に「黒帯エンジニアが教えるプロの技術 Android開発の教科書」がある。