Xcode Cloudで自動テストが回るようになり、コードの見直しをしていたところ、外部APIのアクセストークンをUserDefaultsにそのまま保存していることに気づき、青ざめました。今回はKeychainという仕組みに書き換えた記録です。
1. UserDefaultsに機密情報を入れていた危うさ
これまで手軽さから、ログイン後に受け取ったトークンも設定値と同じ感覚でUserDefaultsに保存していました。
UserDefaults.standard.set(token, forKey: "accessToken")
後から知ったのですが、UserDefaultsはアプリの設定ファイルとして平文に近い形で保存される仕組みで、暗号化を前提とした保存場所ではありませんでした。パスワードやトークンのような機密情報を置く場所としては適していないと理解し、慌てて見直すことにしました。
2. Keychainの生のAPIは呼び出しが長く複雑だった
調べてみると、iOSにはKeychainという暗号化された安全な保存領域が標準で用意されていました。ただし、標準APIをそのまま使おうとすると、辞書形式の設定を組み立てる必要があり、見慣れない書き方に戸惑いました。
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "accessToken"
]
UserDefaultsの1行での書き心地に慣れていた分、同じことをするだけなのに何倍も複雑に感じました。
3. 自作のラッパー関数でシンプルに扱えるようにした
生のAPIを毎回書くのは大変だと分かったので、保存・取得・削除だけを行う簡単なラッパー関数を自分で用意することにしました。
func saveToken(_ token: String) {
KeychainWrapper.save(token, forKey: "accessToken")
}
呼び出し側からはUserDefaultsとほぼ同じ感覚で使えるようにラップしたことで、以降のコードを書き換える負担がぐっと減りました。「安全な保存先だから複雑でも仕方ない」と諦めず、使いやすさのために一枚ラッパーをかぶせるという発想が有効だと学びました。
4. アプリを削除してもデータが残っていて混乱した
書き換えが終わって動作確認をしていたところ、一度アプリを削除して再インストールしたのに、以前保存したトークンがまだ残っていて不具合を起こしました。
Keychainのデータは、アプリを削除しても
端末に残り続けることがある(仕様)
てっきりアプリの削除と同時にKeychainのデータも消えると思い込んでいたのですが、これは仕様であり、意図的な挙動だと知りました。再インストール後の初回起動時には、古いトークンが残っている可能性を踏まえて、有効性を確認してから使う処理を追加することにしました。
まとめ
UserDefaultsは平文に近い保存形式のため、トークンやパスワードなどの機密情報の保存には適していない- Keychainの標準APIは辞書形式で複雑なため、保存・取得・削除をラップした自作関数を用意すると扱いやすい
- Keychainのデータはアプリを削除しても端末に残り続けることがある。再インストール後は古いデータの有効性を確認する
セキュリティに関わる部分は後回しにしがちでしたが、一度きちんと調べて直したことで、安心して機能追加を続けられるようになりました。
次は、アクセシビリティ対応をやってみて分かったことをまとめます。
本記事は生成AI(Claude)を活用して執筆・編集しています。内容は実体験に基づき、執筆者本人が確認・監修しています。


コメント