LASSIC Media らしくメディア
アプリの端末内データ暗号化の実装
LASSIC IT事業部|元請(プライムベンダー)としてシステム保守・運用を受託
この記事のポイント
- 端末内データ暗号化は、通信路の保護や画面キャプチャ対策とは別の論点であり、トークンや認証情報を端末のストレージ上でどう守るかという観点で設計します。
- iOSはKeychainとData Protection、AndroidはAndroid Keystoreを軸に、それぞれ異なる仕組みで鍵とデータを保護します。
- AndroidのJetpack Security(EncryptedSharedPreferences等)は全APIが非推奨化されており、外注先の実装方針を発注前に確認する必要があります。
目次
アプリの端末内データ暗号化とは
アプリの端末内データ暗号化とは、ログイントークン・認証情報・個人情報といった機密データを、端末のストレージに保存する時点で暗号化しておく仕組みを指します。これは保存時暗号化(encryption at rest)と呼ばれる領域で、通信経路の盗聴・改ざんを防ぐ証明書ピンニングや、画面の不正キャプチャ・録画を防ぐ対策とは切り分けて考える必要があります。通信路対策は「データが移動している間」を守り、画面キャプチャ対策は「表示されている瞬間」を守るのに対し、端末内データ暗号化は「端末に保存されている間」を守る領域であり、対策の目的も実装場所も異なります。
iOSとAndroidは、この保存時暗号化を実現するための専用機構をそれぞれOSレベルで提供しています。iOSではKeychainとData Protection、AndroidではAndroid Keystoreを土台にした仕組みが該当します。両者は思想が近い部分もありますが、APIの構造やアクセス制御の粒度は異なるため、片方の設計をそのまま流用すると実装のずれが生じやすい領域です。
法人が扱うアプリでは、社員証代わりの認証トークンや、顧客情報と紐づくID、業務システムへのアクセス権限情報などが端末内に残るケースが少なくありません。端末の紛失・盗難や、リバースエンジニアリングによる解析を想定したとき、平文でストレージに残っているデータがないかを棚卸しすることが、この領域の設計の出発点になります。
iOSのKeychainとData Protection
iOSでは、パスワードや認証トークン、暗号鍵といった少量の機密データを保護して保存する仕組みとしてKeychainが提供されています*1。Keychainはアプリのサンドボックスとは別の保護された領域にデータを保持する仕組みであり、一般的なファイル保存領域とは区別して扱われます。
アクセシビリティ属性でロック状態との関係を制御する
Keychainに保存した項目には、デバイスのロック状態に応じたアクセス可否を定めるアクセシビリティ属性を設定します。代表的な属性の一つであるkSecAttrAccessibleWhenUnlockedは、デバイスがロック解除されている間のみ項目へアクセスできる設定であり、iOSにおける既定のアクセシビリティ属性です*2。この属性を明示的に選ぶことで、端末がロックされている間は該当データへアクセスできない状態を作れます。
属性名の末尾にThisDeviceOnlyが付く種類は、その項目をiCloudキーチェーンによる他デバイスへの同期対象から外す設定です*2。法人向けアプリで発行するトークンのように、端末をまたいで同期させたくない機密情報については、ThisDeviceOnly系の属性を選択するかどうかを実装段階で判断する必要があります。
ファイル単位の保護はData Protection(NSFileProtection)が担う
Keychainが少量の機密データを扱うのに対し、ファイル全体を暗号化して保護する仕組みがData Protectionです。ファイルにはNSFileProtectionによる保護クラスを設定でき、Completeクラスを指定したファイルはデバイスがロックされている間はアクセスできません*3。一方、CompleteUntilFirstUserAuthenticationクラスは、デバイス起動後にユーザーが一度パスコードを入力してロック解除するとアクセス可能になり、その後デバイスを再起動するまではロック中でもアクセスが継続する挙動を取り、これがサードパーティアプリのデータに対する既定の保護クラスです*3。
この2つの保護クラスの違いは実務上重要です。常時ロック中はアクセス不可にしたい高機密ファイルにはCompleteクラスを、バックグラウンド処理などロック中でも一定のアクセスが必要なファイルには既定のCompleteUntilFirstUserAuthenticationクラスを、という判断が必要になります。Keychainで鍵や短い機密情報を守り、Data Protectionでファイル全体を守るという役割分担で理解しておくと設計がぶれません。
鍵そのものをKeychainに保存する設計
暗号化に使う鍵自体をKeychainに保存する実装パターンも用意されています*4。アプリ側で生成した暗号鍵をKeychainに格納し、実際のデータ本体は別途その鍵で暗号化してファイルシステムに保存するという二層構成を取ることで、鍵の管理とデータの保存を分離できます。この構成は、後述するAndroid Keystoreの考え方とも共通する部分があり、モバイルOS側が用意する鍵管理の仕組みに乗せることが、自前実装よりも堅牢性の面で優先されます。
AndroidのKeystoreとEncryptedSharedPreferences・Jetpack Security
Androidでは、暗号鍵の生成と保護をAndroid Keystore systemが担います。Android Keystoreに保存した鍵は、鍵素材そのものがアプリケーションプロセスの外に出ない設計になっており、暗号化・復号などの操作は鍵素材を保持したシステムプロセス側で実行される仕組みです*5。アプリのプロセスが侵害された場合でも、鍵を使った操作は行えても鍵素材自体を抜き出すことはできない点が、この仕組みの中核にあります*5。
ハードウェアに鍵を裏付けるという考え方
Android Keystoreでは、鍵素材をTEE(Trusted Execution Environment)やSE(Secure Element)、StrongBox KeyMintといったセキュアハードウェアに束縛できます*5。セキュアハードウェアに束縛された鍵は、OS自体が侵害されたり内部ストレージが読み取られたりした場合でも、ハードウェアの外に鍵素材が露出しない設計になっています*5。鍵がハードウェアに裏付けられているかどうかは、KeyInfoから確認する手段が用意されており、対応端末やAndroidバージョンによって利用できるセキュリティレベルが異なります*5。
EncryptedSharedPreferences・Jetpack Securityは全APIが非推奨化されている
これまでキーバリュー形式の設定値やファイルを簡便に暗号化する手段として、Jetpack SecurityライブラリのEncryptedSharedPreferencesやEncryptedFileが使われてきました。しかし、androidx.security配下のsecurity-cryptoライブラリは、バージョン1.1.0の安定版リリースにおいて全APIが非推奨化されており、このライブラリの以降のリリースは予定されていません*6。公式のリリースノートでは、既存のプラットフォームAPIとAndroid Keystoreを直接利用する方針への移行が示されています*6。
この非推奨化は、既に稼働しているアプリの実装がすぐに動かなくなることを意味するものではありませんが、新規に実装を発注する場合や既存アプリを改修する場合には、EncryptedSharedPreferencesに依存した設計をそのまま踏襲してよいのかを外注先と確認する必要がある状況です。発注時点でどのライブラリ・APIを採用する想定かを具体的に確認しておくことが、将来の保守コストを左右します。
鍵の生成と暗号化処理をどこで組み合わせるか
Jetpack Securityが担っていた「鍵の管理」と「データの暗号化」という2つの役割は、Android Keystoreで鍵を生成・保護し、暗号化処理自体は標準の暗号APIを直接呼び出すという構成に置き換わりつつあります。iOSのKeychain+Data Protectionの役割分担と同様に、鍵の保護とデータの暗号化を別々の仕組みで担わせる考え方が、両OSに共通する設計思想だと言えます。
何を暗号化し何を保存しないか
端末内データ暗号化の設計でまず整理すべきは、そもそも端末に保存してよいデータかどうかという判断です。認証トークン・APIキー・個人情報のうち、業務上どうしても端末に残す必要があるものと、サーバー側にのみ保持しサーバー問い合わせで代替できるものを切り分けることが最初の工程になります。
トークン類は保存範囲と有効期限を絞る
ログインセッションを維持するためのトークンは、Keychain(iOS)やAndroid Keystoreで保護した鍵による暗号化ストレージ(Android)に保存するのが基本方針です。ただし、保存すること自体がリスクを伴うため、有効期限を短く設定したリフレッシュトークン運用にする、失効・再発行の仕組みをサーバー側に用意するなど、保存する情報の寿命を短く保つ設計が並行して必要です。
鍵は生成場所とエクスポート可否を明確にする
暗号化に使う鍵は、可能な限りOSが提供する鍵管理機構(KeychainやAndroid Keystore)の内部で生成し、アプリのコード内にハードコードしたり、鍵そのものをファイルとして端末に書き出したりしない設計が前提になります。Android Keystoreで生成した鍵は、そもそも鍵素材をエクスポートできない設計になっているため*5、この制約に沿った実装であるかどうかが確認ポイントです。
個人情報は保存有無の判断を業務要件から逆算する
氏名・連絡先・所属といった個人情報を端末内にキャッシュするかどうかは、暗号化の実装以前に、そもそもオフラインで表示する必要があるかという業務要件から判断すべき事項です。オフライン表示が不要であれば、都度サーバーから取得し端末には保存しないという選択肢が、暗号化の実装コストと漏えいリスクの両方を下げます。保存が必要な場合に初めて、どの保護クラスやアクセシビリティ属性を選ぶかという実装の話に進みます。
鍵管理と生体認証・端末ロックとの連携
端末内データ暗号化の実効性は、鍵そのものの保護強度だけでなく、鍵へのアクセスを端末のロック状態や生体認証とどう連携させるかによって左右されます。
iOSはアクセシビリティ属性と認証ポリシーの組み合わせ
iOSでは、Keychain項目のアクセシビリティ属性に加えて、Face IDやTouch ID、パスコード入力といったユーザー認証を要求するアクセス制御ポリシーを組み合わせて設定できます。デバイスがロック解除されている状態でもなお生体認証を求めたい機密データについては、アクセシビリティ属性だけでなく認証ポリシー側の設定まで踏み込んで確認する必要があります。
Androidは鍵使用時の認可設定で制御する
Android Keystoreでは、鍵の生成・インポート時に、その鍵をどのような条件で使用できるかという認可情報を指定でき、こうした認可はアプリプロセスの外側で強制される仕組みです*5。ユーザー認証後の一定時間のみ鍵の使用を許可する、といった時間的な制約を組み込むことも可能で、生体認証の結果と鍵の利用可否を直接結びつけた設計ができます。
OSアップデートと生体認証機構の変化を前提にする
生体認証の仕組みはOSのバージョンアップに伴って仕様が更新されることがあります。鍵管理と生体認証を連携させる実装は、特定のOSバージョンの挙動に強く依存させすぎず、OS標準のAPIが提供する認証結果を素直に利用する設計にとどめておくことが、長期運用における保守性を保つ観点で重要です。
外注時に確認すべき実装範囲と引き継ぎの論点
端末内データ暗号化は、アプリの見た目に現れない領域であるため、外注時の要件定義で明文化されないまま実装が進みがちです。発注側が確認すべき論点を整理します。
自社で内製するには何が必要か
端末内データ暗号化を内製で実装するには、iOSであればKeychainとData Protectionの挙動、AndroidであればAndroid Keystoreの鍵管理モデルという、OSごとに異なる仕組みを正確に理解したエンジニアが必要です。加えて、EncryptedSharedPreferencesの非推奨化のように、プラットフォーム側の推奨実装が変化していく領域でもあるため*6、実装した時点の情報だけに頼らず、継続的に公式ドキュメントを追う体制が求められます。
発注先への確認
提案書に「データは暗号化して保存します」とだけ記載されている場合、iOS側はKeychainのどのアクセシビリティ属性を採用するか、Android側はAndroid Keystoreを直接使うのかEncryptedSharedPreferences等の非推奨ライブラリに依存するのかを具体的に質問する必要があります。トークンや個人情報のうち何を端末に保存し何を保存しないかという方針まで踏み込んで説明できるかどうかが、実装力を見極める手がかりになります。
契約明記
開発会社との契約終了後に別会社や自社へ運用を引き継ぐ可能性がある場合は、どのデータをどの保護レベル(アクセシビリティ属性・保護クラス・Keystoreの認可設定)で暗号化しているかを一覧化した設計資料を納品物に含めるよう、契約段階で明記しておく必要があります。この資料がないまま引き継ぐと、後任の担当者が暗号化実装の範囲をコードから読み解くところから作業を始めることになり、追加の調査コストが発生しかねません。
端末内データ暗号化は一度実装して終わりではなく、OS側の推奨実装が変わるたびに見直しが必要な領域です。発注時にはこの継続的な追随を含めた運用体制まで確認しておくことが、長期的な保護レベルの維持につながります。
まとめ:端末内データ暗号化を機能させる3つの判断軸
本稿では、iOSのKeychain・Data ProtectionとAndroidのAndroid Keystore・Jetpack Securityを軸に、モバイルアプリの端末内データ暗号化について整理しました。要点を3つに集約すると次の通りです。第一に、端末内データ暗号化は通信路対策や画面キャプチャ対策とは異なる論点であり、端末のストレージに保存されている間のデータ保護に的を絞って設計する必要があります。第二に、iOSとAndroidでは鍵とデータの保護の仕組みが異なり、アクセシビリティ属性や保護クラス、鍵使用の認可設定といったOSごとの機構を正確に踏まえた実装が欠かせません。第三に、AndroidのEncryptedSharedPreferences等が非推奨化されたように、プラットフォーム側の推奨実装は変化していくため、外注する場合も採用するAPIの根拠と引き継ぎ資料の範囲を発注前に確認する姿勢が、長期的な保護レベルの維持を左右します。
よくある質問
端末内データ暗号化と通信路の暗号化は何が違いますか。
通信路の暗号化(HTTPS通信や証明書ピンニング)は、データが端末とサーバーの間を移動している最中の盗聴・改ざんを防ぐ対策です。これに対して端末内データ暗号化は、データが端末のストレージに保存されている間の漏えいを防ぐ対策であり、対象とする場面が異なります。両方を組み合わせて初めて、通信中も保存中もデータが保護された状態になります。
iOSのKeychainにはどのようなデータを保存すべきですか。
Keychainはパスワードや認証トークン、暗号鍵といった少量の機密データを保護して保存するための仕組みです*1。画像や大きなファイルの保存には向かないため、そうしたデータはData Protectionの保護クラスを設定した通常のファイルストレージで扱う使い分けが基本です*3。
AndroidのEncryptedSharedPreferencesはもう使えないのですか。
androidx.security配下のsecurity-cryptoライブラリは、バージョン1.1.0の安定版リリースで全APIが非推奨化されており、今後の追加リリースは予定されていません*6。既存の実装がすぐに動作しなくなるわけではありませんが、新規実装や改修の際は、既存のプラットフォームAPIとAndroid Keystoreを直接利用する方針への切り替えを検討する必要があります*6。
Android Keystoreに保存した鍵は取り出して確認できますか。
できません。Android Keystoreで保護された鍵は、鍵素材そのものがアプリケーションプロセスの外に出ない設計になっており、暗号化・復号などの操作はシステムプロセス側で実行されます*5。アプリのプロセスが侵害された場合でも、鍵を使った操作は可能でも鍵素材自体を抽出することはできません*5。
外注先に端末内データ暗号化の実装を依頼する際、何を確認すればよいですか。
まず、端末に保存するデータの種類(トークン・個人情報等)と、それぞれに適用する保護レベル(iOSのアクセシビリティ属性・保護クラス、AndroidのKeystoreの認可設定)を具体的に説明できるかを確認します。あわせて、AndroidでEncryptedSharedPreferences等の非推奨ライブラリに依存していないか、契約終了後の引き継ぎ資料に暗号化の設計内容が含まれるかも、発注前に確認しておくべき論点です*6。
著者:テレリモ総研編集部 鈴木 亮佑
ご不明な点はお問い合わせフォームからもご連絡いただけます。
- *1 出典:Apple「Keychain services」(Apple Developer Documentation)https://developer.apple.com/documentation/security/keychain-services
- *2 出典:Apple「Restricting keychain item accessibility」(Apple Developer Documentation)https://developer.apple.com/documentation/security/restricting-keychain-item-accessibility
- *3 出典:Apple「Encrypting your app’s files」(Apple Developer Documentation)https://developer.apple.com/documentation/uikit/encrypting-your-app-s-files
- *4 出典:Apple「Storing keys in the keychain」(Apple Developer Documentation)https://developer.apple.com/documentation/security/storing-keys-in-the-keychain
- *5 出典:Android「Android Keystore system」(Android Developers)https://developer.android.com/privacy-and-security/keystore
- *6 出典:Android「Security」(Jetpack Releases・androidx.security-crypto)https://developer.android.com/jetpack/androidx/releases/security