🛡️

Android 対象範囲別ストレージの導入と外部ストレージにおけるファイルアクセス制限

2024/03/24に公開

はじめに

Androidアプリの設計・開発では、長らく「外部ストレージの利用は最小限に留め、機密情報の保存は避けるべき」とされてきました。それは、従来のAndroidシステムでは、ユーザが許可を与えたアプリであれば外部ストレージに保存された任意のファイルにアクセスできてしまうためです。仮にデバイス内に不正なアプリがインストールされ、そのアプリに外部ストレージへのアクセス許可が与えられていた場合、他のアプリが外部ストレージに保存した機密情報を参照・改ざんされるおそれがあります。

Androidアプリの脆弱性診断でも、アプリが外部ストレージに保存するファイルの内容精査は必須のチェック事項です。もしファイル内に機密情報が含まれ、何の保護措置も設けられずに保存されていることが判明した場合は、どのセキュリティベンダーでも比較的高い危険度の脆弱性として報告しているのではないでしょうか。

https://mas.owasp.org/MASTG/tests/android/MASVS-STORAGE/MASTG-TEST-0001/

しかし、Android 10 (API Level 29) から導入された「対象範囲別ストレージ」により、一概に外部ストレージへのデータ保存が危険であるとは限らない状況に変わりました。そこで本記事では、対象範囲別ストレージの導入により、外部ストレージに保存されたファイルへのアクセス制限がどう変わり、どのようなリスクが軽減されるのかを解説していこうと思います。

対象範囲別ストレージについては、AOSP(Android Open Source Project)等の公式リファレンスが公開されているものの、やや具体的な解説が少なく、加えて、対象範囲別ストレージについて誤った情報が含まれているブログ記事等も散見されるため、実際のところ何が正しいのかよく分からないという方もいらっしゃるのではないでしょうか。

本記事は、Androidの外部ストレージ仕様について明るくない方でもスムーズに理解が進むよう、基本事項から記載していきますが、わかりづらい、もしくは誤った内容の文章等がありましたら、コメント等でお知らせ頂けますと幸いです。

Androidアプリのデータ保存領域

対象範囲別ストレージの説明に入る前に、まずはAndroidデバイス内でアプリがデータを保存するために利用できる領域の種類について触れておきたいと思います。


Androidアプリのデータ保存領域

保存領域を分類すると、主に以下の4種類となります。

  • 各アプリ専用の内部ストレージ (イラストの赤枠内)
  • 各アプリ専用の外部ストレージ (イラストの青枠左側)
  • メディアファイル用ストレージ (イラストの水色枠上側)
  • ドキュメント・その他のファイル用ストレージ (イラストの水色枠下側)

各アプリ専用のストレージは内部・外部ストレージの両方に確保されていますが、一般的に重要なデータを保存する領域としては、アクセス制限が厳格な内部ストレージの方が適しています。一方、比較的容量の大きなデータを保存する場合やバックアップ等の目的でデバイス外部に持ちだすことが想定されるデータを保存する場合には、外部ストレージの方を利用することがあります。

メディアファイル用のストレージは、アプリ間で共有可能な画像・音声・動画等のメディアデータを保存するための領域です。

最後に、ドキュメント・その他のファイル用ストレージは、Web上からダウンロードしたファイル等のアプリ間で共有可能なコンテンツを保存するための領域です。

保存するデータの性質とそれによって選択すべき保存領域の例は、以下のリファレンスにも記載されています。どのストレージに保存するのかは、主にデータの容量やデータアクセスの信頼性、データを非公開にする必要があるか否かを基に選択するのが一般的です。

https://developer.android.com/training/data-storage?hl=ja

なぜ外部ストレージに機密情報を保存するのが危険なのか(対象範囲別ストレージ導入以前)

では、なぜ外部ストレージに機密情報を保存するのを避けるべきなのでしょうか。それは、本記事の冒頭でも触れた通り、ユーザが許可を与えたアプリであれば外部ストレージに保存された任意のファイルにアクセスできてしまうためです。仮にデバイス内に不正なアプリがインストールされ、そのアプリに外部ストレージへのアクセス許可を与えてしまっていた場合、他のアプリが外部ストレージに保存した機密情報を参照・改ざんされるおそれがあります。


Androidアプリのデータ保存領域 (対象範囲別ストレージ導入以前)

上の図は、アプリAがアプリA専用の外部ストレージに保存したデータに、アプリBもアクセスできてしまうことを表しています。アプリ専用の外部ストレージの他、メディアファイル用ストレージやドキュメント・その他のファイル用ストレージにアプリAが保存したデータについても同様です。

以下の記事にも記載した通り、不正なアプリを被害者のデバイス内にインストールさせるにはある程度のハードルが存在するといえますが、悪意のある第三者が被害者を巧妙に誘導し、正規のアプリを装ってインストールさせるという手法は多く報告されています。また不正アプリをインストールさせることに成功した場合、被害者はそれを正規のアプリであると思い込んでいるため、アプリに対して外部ストレージへのアクセス許可も与えてしまう可能性が高いと考えられます。そして、ひとたびアクセス許可を与えられると、そのアプリはユーザが気づかないうちに外部ストレージ上のファイルにアクセスすることが可能になります。

https://zenn.dev/kaneko_s/articles/383913dbee2d52#2.ユーザがインストールした不正アプリによる脅威

このため対象範囲別ストレージの導入以前は、外部ストレージへのファイル保存に以下のような方針が求められてきました。

  • 漏えい・改ざんされた際のリスクを受容できるデータでない限り、外部ストレージにデータを保存しないようにする。(あるいは、代わりに内部ストレージに保存する。)
  • 外部ストレージに保存せざるを得ない場合は、第三者から推測できない暗号鍵を利用して暗号化したうえで保存する。

https://www.jssec.org/dl/android_securecoding/4_using_technology_in_a_safe_way.html#sd

対象範囲別ストレージとは

前述の通り、従来のAndroidシステムにおける外部ストレージは、アクセス許可さえ与えられていればどのようなアプリでもアクセスできる、いわば公開ディレクトリに近い保存領域でした。それに対して、アプリによる外部ストレージへのアクセスを制限しユーザデータを保護するために Android 10(API Level 29)から導入されたのが「対象範囲別ストレージ」です。(Android 10では対象範囲別ストレージをアプリの設定で無効にすることができたため、本格的な導入は Android 11 からと言えるかもしれません。)

https://source.android.com/docs/core/storage/scoped?hl=ja
https://developer.android.com/about/versions/11/privacy/storage?hl=ja

対象範囲別ストレージの導入以前からAndroidシステムに外部ストレージはありましたが、導入以降は外部ストレージが目的に応じて3つ("各アプリ専用の外部ストレージ"、"メディアファイル用ストレージ"、"ドキュメント・その他のファイル用ストレージ")に厳密に分けられ、アクセス方法と許可の与え方も別々に設定されています。

では、デバイス内にインストールされた不正なアプリによる脅威(ファイル内容の漏えい・改ざん)に対して、対象範囲別ストレージの導入で十分にリスクが軽減されるのでしょうか。以下、各ストレージの種類ごとに、それを見ていきたいと思います。

"各アプリ専用の外部ストレージ" のアクセス制限

上記のリファレンスには、各アプリ専用の外部ストレージに保存されたファイルへのアクセスについて以下のように記載されています。

・No read or write access to other apps' external app data directories

これは、対象範囲別ストレージ導入後は、各アプリ専用の外部ストレージ内のファイルに他のアプリからはアクセスできないよう制限される、ということを示しています。


Androidアプリのデータ保存領域 (対象範囲別ストレージ導入以後)

実際に、以下のようなコードで他アプリ(sk.sampleapp.trainingapp)専用の外部ストレージ内に保存されているファイル(Sample.txt)にアクセスしようとするとExceptionが発生します。

public void readOtherAppExternalStorageData(View view){
    int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
    if (permission == PackageManager.PERMISSION_GRANTED) {
        String file = Environment.getExternalStorageDirectory().getPath() + "/Android/data/sk.sampleapp.trainingapp/files/Documents/Sample.txt";
        try {
            FileInputStream fileInputStream = new FileInputStream(file); // ここでExceptionが発生する
			
            //...(中略)...
			
        } catch (IOException e) {
            e.printStackTrace();
        }
    }else{
        ActivityCompat.requestPermissions(this, this.PERMISSIONS_STORAGE, this.REQUEST_EXTERNAL_STORAGE);
    }
}

W/System.err: java.io.FileNotFoundException: /storage/emulated/0/Android/data/sk.sampleapp.trainingapp/files/Documents/Sample.txt: open failed: EACCES (Permission denied)

このため、対象範囲別ストレージが適用されたデバイス上で動くアプリでは、アプリ専用の外部ストレージが内部ストレージと同等にセキュアになったと言えるでしょう。機密情報を含むデータであっても、データの容量が大きく内部ストレージへの保存に適さないような場合、そのデータをアプリ専用の外部ストレージに保存するのも有効な選択かと思います。

"メディアファイル用ストレージ" のアクセス制限

メディアファイル用ストレージへは、MediaStore API を利用してアクセスします。MediaStore API では、メディアファイル用ストレージに保存されたファイルが Android OS によってデータベース管理され、ContentResolver からそのファイルのURIを取得し、参照・書込み等のファイル操作を行います。

上記のリファレンスには、メディアファイル用ストレージへのアクセスについて以下のように記載されています。

・Read access to other apps' media files with READ_EXTERNAL_STORAGE permission
・Write access to other apps' media files is allowed only with direct user consent (exceptions granted to System Gallery and apps that are eligible for All Files access)

これは、対象範囲別ストレージ導入後は、他のアプリが保存したメディアファイルを参照するには「READ_EXTERNAL_STORAGE」権限が、書き換えるにはユーザから個別に承認を得なければならない、ということを示しています。

つまり、もしデバイス内にインストールされた不正なアプリに「READ_EXTERNAL_STORAGE」権限が与えられていると、他アプリが保存したメディアファイルを秘密裏に参照できる、ということです。以下の記事では、MediaStore APIを利用してメディアファイルを操作する方法が分かりやすく解説されています。実際に手元で確認したい場合は参考になるかと思います。
https://qiita.com/irgaly/items/0ce450dd77bd54fb560a

一方、ファイルを改ざんするにはユーザから個別に承認を得なければならないため、不正なアプリが秘密裏にファイル内容を改ざんするというのはできないと考えられます。以下のリファレンスには、他のアプリが保存したメディアファイルのURIを指定して書込み等のファイル操作を行うサンプルコードが記載されています。

https://developer.android.com/training/data-storage/shared/media?hl=ja#manage-groups-files

記載されたように PendingIntent を送信すると、以下のようにユーザに承認を求めるダイアログが表示され、ここで承認しない限りファイル操作ができないことが分かります。

なお、MediaStore API を利用せず、前述の「"各アプリ専用の外部ストレージ" のアクセス制限」に記載したコード例のようにファイルのパスを指定してFileオブジェクトを生成し、それに対して参照・書き換え操作ができないかを試みたところ、以下のような結果となりました。このことから、ファイルの直接操作を試みた際も、MediaStore APIを利用する際と同等のアクセス制限がかかるといえるでしょう。

  • 他アプリが保存したメディアファイル(/sdcard/Pictures配下)の参照
     … 自アプリに「READ_EXTERNAL_STORAGE」権限が無い場合 → Exception (open failed: EACCES (Permission denied)) が発生する
     … 自アプリに「READ_EXTERNAL_STORAGE」権限が有る場合 → 参照できる
  • 他アプリが保存したメディアファイル(/sdcard/Pictures配下)の書換え → Exception (open failed: EACCES (Permission denied)) が発生する

"ドキュメント・その他のファイル用ストレージ" のアクセス制限

"ドキュメント・その他のファイル用ストレージ"へは、ストレージアクセスフレームワーク経由でアクセスします。ストレージアクセスフレームワークでは、システムが提供するファイル選択ツールを使用してユーザーがアクセスするファイルの選択を行います。

このため、他アプリが保存したドキュメント・その他のファイルを秘密裏に参照・改ざんするというのはできないと考えられます。以下のリファレンスには、ストレージアクセスフレームワークを利用してドキュメント・その他のファイルの操作を行うサンプルコードが記載されています。

https://developer.android.com/training/data-storage/shared/documents-files?hl=ja

記載されたように Intent を送信すると、以下のようにユーザにファイル選択を求めるダイアログが表示され、ここでファイルを選択しない限りファイルを操作できないことが分かります。

なお、ストレージアクセスフレームワーク を利用せず、前述の「"各アプリ専用の外部ストレージ" のアクセス制限」に記載したコード例のようにファイルのパスを指定してFileオブジェクトを生成し、それに対して参照・書き換え操作ができないかを試みたところ、Exception (open failed: EACCES (Permission denied)) が発生しました。このことから、ファイルオブジェクトの直接操作によって不正なアプリが秘密裏にドキュメント・その他のファイル用ストレージ内のファイルにアクセスすることはできないといえます。

まとめ

ここまで、対象範囲別ストレージの導入による各ストレージに保存されたファイルへのアクセス制限と、それが不正なアプリからの秘密裏に攻撃を受ける脅威を軽減できるかを解説してきました。その対応表をまとめると以下のようになります。

ストレージの種類 不正アプリによるファイル参照 不正アプリによるファイル改ざん
各アプリ専用の外部ストレージ 不可 不可
メディアファイル用ストレージ 不正アプリに「READ_EXTERNAL_STORAGE」権限があれば可能 不可
ドキュメント・その他のファイル用ストレージ 不可 不可

上記の通り、対象範囲別ストレージの導入により、不正アプリによる脅威はかなり軽減されたものの、"メディアファイル用ストレージ"にファイルを保存する際は引き続きリスクが残存するため、注意が必要であると考えられます。対象範囲別ストレージ導入後も、"メディアファイル用ストレージ"には機密情報を含むファイルは保存しないほうがよいでしょう。

なお、上記はあくまで対象範囲別ストレージが適用されたデバイス上で動くアプリでの話であることにご注意ください。Android 10(API Level 29)のデバイスでは対象範囲別ストレージをアプリの設定で無効化できる、また、それ未満のデバイスでは対象範囲別ストレージがそもそも適用されないので、外部ストレージに保存されたファイルに他アプリからアクセスできてしまいます。

これらの点を考慮すると、外部ストレージに機密情報を保存してもセキュリティ上問題ないと言えるアプリは、対象範囲別ストレージが適用される Android 11(API Level 30)以降のデバイスにだけインストールできるアプリで、かつ、データを"アプリ専用の外部ストレージ"や"ドキュメント・その他のファイル用ストレージ"に保存するケースのみ、ということになりそうです。Android 11 (API Level 30) のリリースは2020年9月であるため、現時点(2024年3月)ではまだそのようなアプリはほとんどないかもしれません。

Android 11 (API Level 30) 未満をサポートせざるをえないアプリでは、引き続き、外部ストレージに機密情報を保存しないようにするか、アプリが動作するデバイスの API Level に応じて処理を分岐する(対象範囲別ストレージが適用されている場合はアプリ専用の外部ストレージに保存し、適用されていない場合は外部ストレージへの保存を避ける等)、あるいは暗号化した上で外部ストレージに保存する等の処置をする必要がありそうです。

対象範囲別ストレージ導入によりセキュリティは向上したものの、残念ながらそれで万事問題が解決されるというわけではない、ということを見て取れたかと思います。

本記事は、私の知る限りの情報を、可能な限り誤解を生まないよう慎重に記載しましたが、もしかしたら情報の抜け漏れや誤った理解が含まれているかもしれません。恐れ入りますが、そのような記載がありましたら、コメント等でお知らせ頂けますと幸いです。

Discussion