プライバシーファースト設計:メモアプリにおける偏執的なこだわり
1. なぜメモアプリにプライバシーが重要なのか
仕事のアイデア、個人的な下書き、日々の記録。メモアプリには、人に見せる前の情報が集まる。素早く書けることと同じくらい、どこに保存され、どこへ送られるかを理解できることが大切だ。
この記事では、端末内の暗号化、通信キャッシュ、画面表示、診断ログと利用解析を分けて説明する。ひとつの保護が、ほかの経路の保護も保証するわけではない。
2. アプリスイッチャーでメモを自動的に隠す機能は有効ではない
iOSは、アプリがバックグラウンドへ移った後の画面をスナップショットとして使う。Appleは、画面に機密情報がある場合は背景へ移る際に取り除くよう案内している。
確認した5.8.17のソースでは、sceneWillResignActiveとsceneDidEnterBackgroundはドラフト保存を行い、プライバシーオーバーレイの表示処理を呼ばない。以前の表示用関数は残っているが、有効な保護として動作する経路ではない。
表示中のメモがアプリスイッチャーの画像に残る可能性がある。Outboxを暗号化していても、画面画像が隠れるわけではない。この記事の旧版にあった「常にメモ内容を隠す」という説明を訂正する。
3. ephemeral URLSession:通信キャッシュのディスク保存を抑える
標準のURLSessionは、キャッシュ、Cookie、認証情報をディスクに保存する。一般的なアプリではこれは便利な機能だが、メモアプリでは不要であり、リスクになりうる。
通信キャッシュが端末に残るということは、どのURLにいつアクセスしたかの痕跡がディスク上に存在するということだ。Cookieが保存されるということは、セッション情報が端末に残るということだ。
このアプリでは、URLSessionConfiguration.ephemeralを使用する。
private let session: URLSession = {
let config = URLSessionConfiguration.ephemeral
config.httpCookieAcceptPolicy = .never
config.httpShouldSetCookies = false
config.urlCache = nil
return URLSession(configuration: config)
}()
- ディスクキャッシュなし:このURLSessionのキャッシュをディスクに保存しない
- Cookieの利用を無効化:このURLSessionではCookieを受け入れず、要求にも設定しない
- URLキャッシュ無効化:キャッシュストレージ自体を
nilに設定 - 永続ストレージを使わない構成:URLSessionのセッション情報をディスクへ保存しない
これはURLSessionのキャッシュなどに関する保護であり、アプリが別途保存・送信する利用イベントや、サーバー側の送信記録をなくす機能ではない。利用解析の識別子による関連付けも、この設定とは別に行われる。
4. 性能計測ログの出力範囲
PerformanceLoggerのログ出力は、#if DEBUGで囲まれている。DEBUGが定義されないビルドでは、このロガーのログ出力処理は含まれない。
このロガーのlogErrorは、エラーの詳細文ではなく型を記録する。たとえばURLErrorという種類を使い、ファイルパスやユーザー入力を含む可能性のあるlocalizedDescriptionをそのまま出さない。
一方、logEventに渡すイベント名や追加文字列を自動ですべて無害化する仕組みではない。呼び出し元でメールアドレス・メモ本文・認証情報を渡さないことが必要になる。
この説明を、アプリ・外部SDK・OS全体の「ログゼロ」という保証には広げない。自社の利用イベント送信とAppsFlyerは別の仕組みとして有効になっている。取得範囲は本記事の利用解析FAQとプライバシーポリシーで説明する。
5. AES-GCM暗号化Outboxの保護範囲
送信待ちのメモは、端末内のOutboxというキューで管理する。保存時にはAppleのCryptoKitを使ってAES-GCMで暗号化する。新しい暗号鍵は256ビットで生成する。
鍵はKeychainに保管し、kSecAttrAccessibleAfterFirstUnlockThisDeviceOnlyを使う。この設定では、再起動後の初回ロック解除から次の再起動までアクセスでき、別の端末へのバックアップ復元では鍵が移行しない。アプリは暗号化・復号のために鍵を読み出す。
これは保存データの保護であり、表示中の画面を隠したり、あらゆる端末侵害からの安全を保証したりするものではない。機種変更前には、必要なメモが保存先に届いていることを確認してほしい。
メール送信を使う場合、本文は当社のRelay APIと配送基盤で処理され、受信箱にはメールとして残る。端末内の暗号化は、メール配送のエンドツーエンド暗号化(E2EE)を意味しない。詳細はプライバシーポリシーとデータの通り道で説明している。
送信待ちキューについては、Outboxアーキテクチャの解説を参照。
6. メモ本文の保護と外部SDKの役割
iOSアプリ内では、メモ本文の暗号化にCryptoKit、鍵保管にKeychain、本文の送信にURLSessionを使う。Network.frameworkは接続状態の監視、BackgroundTasksはバックグラウンド送信のスケジューリングを担う。
一方、アプリは外部SDKも使う。GoogleSignInとFirebase AuthenticationはGoogleアカウントによる任意のメール自動入力に使い、Firebase App Checkは起動時に初期化してバックエンドAPIを保護する。AppsFlyerは獲得・利用解析に使い、自社解析と共通のインストール識別子を設定する。
メモ本文をイベント項目に含めない設計でも、識別子や送信記録の関連付けは残る。SDKを含む利用解析の範囲は、本文の暗号化とは別に確認する必要がある。項目と用途はプライバシーポリシーで説明している。
7. 保護の範囲を明示する
端末内の暗号化、通信キャッシュの扱い、利用解析は、それぞれ異なる役割を持つ。Outboxの暗号化やephemeral URLSessionがあっても、利用イベントの収集がなくなるわけではない。
メモを素早く残せることと、取得する情報を理解できることの両方を大切にする。利用解析の送信先、識別子、関連付けの範囲を説明し、アプリの変更に合わせて見直す。
よくある質問
参考文献
- Apple Developer — CryptoKit Documentation — AES-GCM暗号化によるOutboxデータ保護の実装に使用
- Apple Developer — URLSession Documentation — エフェメラル(一時的)セッションによるネットワーク通信でプライバシーを確保
- Apple Developer — Preventing Insecure Network Connections (App Transport Security) — TLS通信の強制によるデータ転送時のセキュリティ確保
- Apple Developer — Generating Log Messages from Your Code — ログの保存範囲と機密情報の扱い
- Apple Developer — Preparing Your UI to Run in the Background — アプリスイッチャー用スナップショットと画面上の情報
- Apple Developer — Keychain Access After First Unlock, This Device Only — 鍵のアクセス条件と他端末への移行制限