本記事は『Androidアプリ開発のためのセキュリティ入門 体験して学ぶ16の脆弱性とその対策』の「第3章 マニフェストファイルとアプリコンポーネントにおける脆弱性と対策」から一部を抜粋したものです。掲載にあたって編集しています。
Androidアプリとアプリコンポーネント
Androidアプリでは様々な機能を実現するためにアプリコンポーネントを利用しています。アプリコンポーネントとは、アプリを構成する部品要素となるものです(これ以降はコンポーネントと呼びます)。各コンポーネントは、システムやユーザがアプリを操作するためのエントリポイントとなります。
一般的に多くのAndroidアプリは、様々なコンポーネントを組み合わせて作られています。代表的なコンポーネントとして、アクティビティ、サービス、ブロードキャストレシーバ、コンテンツプロバイダなどが存在します。各コンポーネントの詳細は後述します。
各コンポーネントを利用することでAndroidアプリはユーザとのやり取りやバックグラウンドでの処理を実現しています。また、アプリ間でのデータの共有や呼び出しといったアプリ連携の機能もコンポーネントの機能を用いて実現されています。どのようにコンポーネントを利用するかはマニフェストファイルの設定を通じて行うことができます。
アプリコンポーネントとマニフェストファイル
マニフェストファイルとは、アプリに関する重要な情報が記載されているファイルのことです。このファイルには、アプリのパッケージ名やコンポーネント、利用するパーミッション、ネットワークの設定などの情報を記載します。これらの設定情報は、AndroidのオペレーティングシステムやGoogle Playなどに対して、「このアプリがどのようなものなのか」を説明するために利用します。
Androidのオペレーティングシステムはこのファイルを参照することで、アプリの構成や、他のアプリからのアクセスの可否、アプリが利用や要求するパーミッションを把握します。通常、このマニフェストファイルは、Androidアプリのルート直下にAndroidManifest.xmlというファイル名で配置されています。マニフェストファイルはXML形式のファイルとなっており、定義されているXMLタグを利用してアプリに関する設定を表現します。
先ほど説明したコンポーネントもこちらで設定されます。ここからは代表的な4つのコンポーネントについて説明します。
アクティビティ(Activity)
アクティビティは、ユーザ操作を受け付けるアプリの画面となるコンポーネントです。Google Chromeのアプリを例に考えてみましょう。アプリを開くと、まずGoogleの検索画面が表示され、この画面でユーザは情報の検索ができるようになります。
このように、ユーザがアプリを起動した際に実際に画面として表示され、操作ができるコンポーネントがアクティビティです。ユーザが直接操作できるため、アクティビティはフォワグラウンド上で起動されるコンポーネントとなります。アクティビティコンポーネントは、マニフェストファイルで<activity>タグで表現されます。
サービス(Service)
サービスは、バックグラウンドで長時間動作や処理を行うコンポーネントです。ユーザが他のアプリなどに切り替えても、動作が停止することなく実行されるという特徴を持っています。そのため、音楽の再生やインターネットとの通信など、時間がかかる処理を実行する際に利用されるコンポーネントです。サービスコンポーネントは、マニフェストファイルでは<service>タグで表現されます。
ブロードキャストレシーバ(BroadcastReceiver)
ブロードキャストレシーバは、Androidのシステムや他のアプリからの何らかのイベントを受信できるコンポーネントです。このコンポーネントを利用することで、端末の状態(USB関連のイベントや充電状態の変化など)や、サービスで実行した処理の終了結果を取得できます。ブロードキャストレシーバはマニフェストファイルでは<recevier>タグで表現されます。ただし、現代ではマニフェストファイル上での宣言ではなく、ソースコード上で動的に登録される場合が多いです。
コンテンツプロバイダ(ContentProvider)
コンテンツプロバイダは、アプリが持つファイルやストレージ、データベースなどの内部のデータを他のアプリに共有できるコンポーネントです。通常、AndroidアプリはAndroidのサンドボックスシステムにより、他のアプリが持っている内部のデータに対するアクセスを制限されています。しかし、コンテンツプロバイダを利用すれば、他のアプリにデータを共有できるようになります。コンテンツプロバイダはマニフェストファイルでは、<provider>タグで表現されます。
ここまで説明した各コンポーネントの特徴をまとめます(表1)。
アプリで各コンポーネントを利用するには、マニフェストファイルに定義する必要があります。前述したようにコンポーネントの宣言や設定は各コンポーネント名を表現したXMLタグで行われます。例えば、TestActivityというアクティビティを宣言したい場合は、マニフェストファイルに<activity>タグを記載し、タグ内にname属性で名前を設定します。(リスト1)
<manifest package="com.test.example">
<application>
<activity android:name=".TestActivity" android:exported="false">
</activity>
</application>
</manifest>
コンポーネントへの各設定は、XMLタグ内の属性を用いて設定されます。例えば、他のアプリからコンポーネントへのアクセス可否の制御はexported属性を用いて行います。他に設定できる属性は、各コンポーネントによって異なります。利用できる設定を調べたいときは、Android Developersで各コンポーネントタグのドキュメントを確認してください。
パーミッションとマニフェストファイル
マニフェストファイルでは、アプリコンポーネントの他にアプリが利用できるパーミッションを設定できます。パーミッションとは、Androidアプリがスマートフォンの機能やデータ(カメラ、位置情報、連絡先など)にアクセスする際に、ユーザから許可を得るための仕組みです。
Androidでは、アプリがカメラやインターネット、連絡先などのデータにアクセスする際、アプリがデータを利用してよいかどうか、ユーザに確認します。パーミッションの種類には、インストール時に権限を確認するnormal、実行時にユーザに権限を確認するdangerous、専用のリクエストによって呼び出されるappopなどが存在します。
アプリがパーミッションで制限されている機能を利用するには、マニフェストファイルで事前に利用するパーミッションを宣言する必要があります。システムは、マニフェストファイルに記載されている「要求されたパーミッション」において必要があればユーザに確認し、許可されれば使えるようになります(インターネット通信など、ユーザの許可なしで利用できるパーミッションもあります)。
このような、パーミッションの利用の設定は<uses-permission>タグで宣言します。例えば、カメラの権限を要求する際にはマニフェストファイルで以下のように宣言します。
<uses-permission android:name="android.permission.CAMERA"/>
また、デフォルトのパーミッション以外にもアプリ独自のパーミッションが必要な場合、カスタムパーミッションを作成できます。カスタムパーミッションは、
Androidアプリにおけるパーミッションは、カメラや写真など特定の機能を利用するためだけでなく、アプリ内のコンポーネント間のアクセス制御にも使われます。例えば、特定のパーミッションを持つアプリだけが、あるコンポーネントにアクセスできるように制限することが可能です。
これは、コンポーネントのタグ内でpermission属性を設定することで実現できます。また、ここで定義するパーミッションには、開発者が独自に定義したカスタムパーミッションを利用することも可能です。
マニフェストファイルの設定が及ぼす脅威
これまで、アプリコンポーネントやパーミッションについて説明し、それらの利用やアクセス制御の設定でマニフェストファイルが利用されることを解説してきました。マニフェストファイルを活用することで、様々なAndroidにおける機能や他のアプリからの利用を明示的に宣言できます。
しかし、アプリに多くの権限やアクセスを付与すればよいというわけではありません。権限が多いほど利便性は向上しますが、その分セキュリティリスクも高まります。そのため、不要な権限を与えないことでリスクを最小限に抑える「最小権限の原則」という考え方が重要になります。
最小権限の原則とは、アクセス権限の運用に関する考え方の1つであり、必要最低限の権限しか与えないようにすることで、セキュリティリスクを減らせるというものです。なぜ、このような考え方が提唱されているのか理解するために、例を挙げて考えてみましょう。
例えば、カードキーを利用して部屋に入れる施設があったとします。この施設には、部屋Aと部屋Bが存在します。Aさんは作業の関係上、部屋Aに入れる必要があります。部屋Bには入る必要がありません。しかし、カードキーには部屋Aと部屋Bの両方に入れる設定がされていました。部屋Bに入る必要がないAさんにとって、この設定は不要といえます。しかし、この不要な権限の設定は、これまで特に問題になっていませんでした。
ある日、Aさんはカードキーをどこかになくしてしまいました。なくしたカードキーは運悪く、悪意を持った人の手に渡り、部屋Aのみだけではなく、部屋Bにも侵入された結果、両方の部屋からものが盗まれてしまいました。これは、本来不要だった部屋Bに入れる権限を、Aさんに対して過大に与えてしまったことが、被害拡大の要因です。もし、必要最低限の部屋Aへの入室権限のみをカードキーに設定していれば、盗難被害は部屋Aだけに抑えることができたでしょう(図1)。これが、最小権限の原則の考え方です。
これは現実世界だけではなく、アプリの世界でも同様です。利用していないのに過大な権限を付与することは、その権限が悪用された際に被害を拡大させる恐れがあります。また、悪用されるリスクをゼロにはできないので、悪用された際に被害を最小限に抑えるために備えておくことが重要です。そのためにも、アプリ開発でもアクセス制御や権限周りの設定を考える際は、最小権限の原則をもとに必要な権限のみを与えているか考える必要があります。
ここまでで説明した通り、セキュリティの世界では、最小権限の原則の考え方が非常に重要になります。Androidアプリのコンポーネントのアクセス制御を行うマニフェストファイルでも同様の注意が必要です。もし、マニフェストファイルの設定に不備があった場合、悪意あるアプリがコンポーネントや機能を悪用できてしまいます。その結果、想定していないコンポーネントへのアクセスやアプリ処理の実行、コンポーネントに対する不正利用、アプリが持つデータの漏洩につながります(図2)。
脆弱性 意図しないアクティビティの公開
マニフェストファイルの設定ミスにより意図しないアクティビィが公開されることで発生する問題について解説していきます。
通常、モバイルアプリを構成するアクティビティなどのコンポーネントは、OSのサンドボックス機能によって、他のアプリからアクセスできないようになっています。他のアプリから操作できるようにするには、マニフェストファイルの設定を変更する必要があります。
この設定は、マニフェストファイル内の各コンポーネントタグにあるexported属性を変更することで行えます。アプリのマニフェストファイルでexported属性が「true」に設定されている場合、そのコンポーネントは他のアプリに公開され、アクセスが許可されます。逆に、「false」に設定されている場合、非公開となり他のアプリからはアクセスできません。
例えば、リスト2のようなマニフェストファイルで定義されているアプリがあったとします。この場合、Activity_Aのexported属性は「false」に設定されているので、非公開となり、他のアプリからはアクセスできません。逆に、Activity_Bはexported属性が「true」に設定されているため、公開設定となり、他のアプリからアクセス可能となります。
<activity android:name=".Activity_A" android:exported="false"/> <activity android:name=".Activity_B" android:exported="true" />
アプリの機能として他のアプリからのコンポーネントアクセスを想定し、適切なセキュリティ対策を実装している場合は、公開しても問題ありません。しかし、不要な公開設定や不適切なアクセス制御が行われている場合、セキュリティ上の問題が発生します。主に下記の3つの脅威が考えられます。
- 想定していない処理の実行
- 意図していない画面へのアクセス
- 他の攻撃の足がかりとしての利用
それぞれ説明していきます。
想定していない処理の実行
アプリが本来想定している処理やロジックを回避されてしまうという脅威です。具体的なケースとして特定の認証処理を回避して処理を実行できてしまう場合が挙げられます。
特定の認証処理には、「パスコード認証」や「生体認証」が挙げられます。例えば、あるアプリAでパスコード認証が設定されており、認証成功時に特定の画面に遷移して処理が実行できるようになっていたとします。しかし、認証後の画面であるアクティビティAにおいてexported属性がtrueに設定されていました。このような場合、アクティビティAを直接呼び出すことで、本来想定されていた認証プロセスを回避できてしまいます。
また、認証の回避により想定外の処理の実行やデータの不整合を引き起こす恐れもあります。その他にも悪意あるアプリがこの仕組みを悪用して、不正な変更操作を促すフィッシングなどの攻撃に利用する可能性もあります。
意図していない画面へのアクセス
通常のアプリの利用では提供していない画面にアクセスされるという脅威です。例えば、開発中の画面のコンポーネントを誤ってtrue に設定してしまった場合、実際の操作画面上にリンクがないにもかかわらず、その画面が直接呼び出されてしまう可能性があります。その結果、画面上の情報を通じて機密情報が読み取られる恐れがあります。また、仮にその画面自体に直接の問題がなくても、攻撃者はこのアクセスを足がかりに別の攻撃につなげる可能性もあります。
他の攻撃への足がかりとしての利用
最後に、公開されているコンポーネントを利用して、様々な攻撃につなげる脅威もあります。具体的には、悪意あるアプリがこのコンポーネントを呼び出すことで、想定しない挙動をさせたり、不正な値を送信して別の攻撃につなげて機密情報を取得したりする恐れがあります。
例えば、アプリAと独自のカスタムパーミッションを用いて連携しているアプリBがあったとします。アプリAでは、公開コンポーネントを持っており、受け取った値をアプリBに転送する処理を行っています。通常であれば、アプリBのコンポーネントは独自パーミッションで制御されているため、他のアプリが対象のコンポーネントに直接、値を渡すことはできません。
しかし、攻撃者が準備したアプリは、不正な値をアプリAの公開コンポーネントを経由させることで間接的にアプリBに送信できます。もし、アプリBに脆弱性がある場合、アプリAの公開コンポーネントを踏み台にしてアプリBに影響を与えることが可能です。このようにアプリAでコンポーネントが公開されていることで連携している他のアプリに対しても影響を与えるケースがあります。コンポーネントの公開設定がもたらす脅威をまとめたのが図3です。
ハンズオンアプリで体験しながら学べる
本書ではこのあと、ハンズオンアプリを利用して学習を進めていきます。脆弱性の見つけ方や対策方法を実際に体験しながら学べるので、ぜひご活用ください。


