iOSでデータの安全なストレージ入門

機密ユーザーデータを保護することは、iOS アプリケーションの基本的責任です。認証トークン、暗号化キー、またはプライベート認証情報を保存する場合でも、プラットフォームは専用のハードウェア・バック・ソリューションを提供します。キーチェーン]。 ]]またはプロパティ・リスト・ファイルとは異なり、Keychainはデータを残りに暗号化し、厳格なアクセス・コントロールを実行します。 この記事では、Keychainのセキュリティ・アプリケーションの実装、およびネイティブ・フレームワークの最適化、およびネイティブ・フレームワークの最適化、およびネイティブ・フレームワークの最適化、およびネイティブ・アプリケーション・アプリケーション・アプリケーション・アプリケーション・アプリケーション・アプリケーション・アプリケーション・アプリケーション・アプリケーション・アプリケーション・プログラムの最適化、およびセキュリティ・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・プログラム・

iOSのキーホルダーを理解する

Keychainは、オペレーティングシステムによって管理される安全なストレージコンテナです。暗号化されたデータベース内のパスワード、暗号化キー、または証明書などの、小さな機密アイテムを保存します。デバイスがロックされた場合でもKeychainに書かれたデータは保護されます。 主な機能は次のとおりです。

  • ハードウェア対応 AES-256 を使用した、残り[での暗号化。
  • []デバイスパスコード、タッチID、フェイスIDを介してアクセス制御[]]]。
  • []アプリのリインストールを横断する機能[(設定されている場合)とオプションのiCloud同期。
  • []: デフォルトでは、Keychain アクセスグループを共有しない限り、別のアプリのKeychainアイテムを1つ読み込むことはできません。

キーホルダーは、大括的には設計されていません。各項目は数キロバイトで保持します。より大きなデータの場合、 API または フレームワークを使用してファイルベースの暗号化で検討してください。

Keychainサービス API とサードパーティのライブラリ

Appleは、強力で動詞である、ネイティブ[キーチェーンサービス API(Cベース、)を提供します。 直接使用したり、Swiftフレンドリーなラッパーを採用したりすることができます。 ]のような人気のサードパーティのライブラリ ]またはSwiftKeychainWrapper]がボイラーを削減しますが、 一般的にはAPIをデバッグする必要があります。 重要なAPIは、通常は、APIをデバッグする必要があります。

Keychainストレージの設定

何かを格納する前に、 ]キーホルダー項目クラスに決めなければなりません。 一般的なパスワードの最も一般的なのはです。 インターネットパスワードや証明書の場合、他のクラスがあります。 各項目は、項目を説明する辞書(辞書)の属性のセットで参照されます。

基本の流れは、常にこのパターンに従います。

  1. クエリ辞書を項目クラスと属性で作成します。
  2. 関数 (]), [], , []) を呼び出します。
  3. 返された[ () か、エラーコード) を確認してください。

コードを書く前に、セキュリティモジュールをインポートします。

import Security
import Foundation // for Data and String utilities

Keychain のデータ保存

一般的なパスワードを書く

現在のユーザのトークン(JWTなど)を保存するために:

func saveToken(_ token: String, forAccount account: String) -> Bool {
 guard let tokenData = token.data(using: .utf8) else { return false }

 let query: [String: Any] = [
 kSecClass as String: kSecClassGenericPassword,
 kSecAttrAccount as String: account,
 kSecValueData as String: tokenData,
 // Optional: restrict access to when device is unlocked
 kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
 ]

 // Delete any existing item first to avoid duplicates
 SecItemDelete(query as CFDictionary)

 let status = SecItemAdd(query as CFDictionary, nil)
 return status == errSecSuccess
}

重要なポイント:

  • [ は、主キーとして機能します。 ユニークな文字列(例えば、ユーザ ID または のような定数を選択します。)を選択します。
  • は、項目が読み取れるときに制御します。 を使用して、最高のセキュリティのために、iCloudバックアップを防ぎ、現在のデバイスへのアクセスを制限します。
  • 重複したアイテムを蓄積しないようにするには、[]を呼び出します。 または、を使うことができます。

アクセス制御(バイメトリまたはパスコード)の追加

機密性の高いデータについては、読み込む前にタッチIDまたはフェイスIDが必要です。

let accessControl = SecAccessControlCreateWithFlags(
 nil,
 kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
 .userPresence, // requires passcode, Face ID, or Touch ID
 nil
)

let query: [String: Any] = [
 kSecClass as String: kSecClassGenericPassword,
 kSecAttrAccount as String: account,
 kSecValueData as String: tokenData,
 kSecAttrAccessControl as String: accessControl as Any
]
SecItemAdd(query as CFDictionary, nil)

今、この項目の [ 呼び出しは、生体認証またはパスコードプロンプトをトリガーします。 LocalAuthentication から を使用して、ユーザのやり取りを適切に処理します。

Keychainからのデータを取得する

保存されたトークンを読んでください。

func retrieveToken(forAccount account: String) -> String? {
 let query: [String: Any] = [
 kSecClass as String: kSecClassGenericPassword,
 kSecAttrAccount as String: account,
 kSecReturnData as String: true,
 kSecMatchLimit as String: kSecMatchLimitOne
 ]

 var item: CFTypeRef?
 let status = SecItemCopyMatching(query as CFDictionary, &item)

 guard status == errSecSuccess,
 let data = item as? Data,
 let token = String(data: data, encoding: .utf8) else {
 return nil
 }
 return token
}

を ] にセットして、データを戻します。 ] を使用して、単一の結果を取得します。 制限を省略した場合は、API は配列を返すことがあります。

[] 重要:]]] のアクセス制御(ビオメトリー)を使用する場合、 は、ユーザーがキャンセルした場合、[を返す可能性があります。 このケースを個別に扱い、テキストストレージを平らに返すことは決してありません。

キーホルダーアイテムの更新と削除

既存の項目の更新

削除と再追加の代わりに、[]を使用します。

func updateToken(_ newToken: String, forAccount account: String) -> Bool {
 guard let newData = newToken.data(using: .utf8) else { return false }

 let query: [String: Any] = [
 kSecClass as String: kSecClassGenericPassword,
 kSecAttrAccount as String: account
 ]

 let attributesToUpdate: [String: Any] = [
 kSecValueData as String: newData
 ]

 let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
 return status == errSecSuccess
}

これは、delete+addよりも効率的で、潜在的なレース条件を避けます。

アイテムの削除

func deleteItem(forAccount account: String) -> Bool {
 let query: [String: Any] = [
 kSecClass as String: kSecClassGenericPassword,
 kSecAttrAccount as String: account
 ]
 let status = SecItemDelete(query as CFDictionary)
 return status == errSecSuccess
}

共有キーチェーンを使用する場合は、他のアプリに所属するアイテムを削除しないでください。また、共有キーチェーンを使用する場合は、 でクエリをスコープ化します。

アクセス制御とアクセシビリティ属性

定数で を定義します。] は、Keychain アイテムを読み取ります。アプリのニーズを満たす最も制限的なオプションを選択します。

AttributeMeaning
kSecAttrAccessibleWhenUnlockedAvailable only while device is unlocked (default).
kSecAttrAccessibleAfterFirstUnlockAvailable after device boots and is unlocked once. Allows background access.
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlyRequires a passcode to be set. Strictest option—prevents access even after unlock if passcode is removed.
kSecAttrAccessibleWhenUnlockedThisDeviceOnlySame as WhenUnlocked but does not back up to iCloud, and cannot be restored to another device.

ほとんどのアプリでは、セキュリティとユーザビリティの正しいバランスを打つ。 背景にある項目を読んでいる必要がある(例えば、背景リフレッシュトークン)、あなたは使用する必要があります (そして、データが少し保護されていないことを受け入れます)。

エラー処理と一般的な落札

[] 関数は ] を返します。 常にそれを確認し、適切な失敗を処理します。 一般的なエラー:

  • (–25300) - クエリに一致する項目はありません。
  • (–25299) - 既に同じ主キーを持つ項目(最初に削除しなかった場合)。
  • (–128) - ユーザは生体測定プロンプトをキャンセルしました。
  • (–25293) - 認証が失敗または生体認証が利用できなくなった。

成功しない状態を無視しないでください。 うまく劣化: エラーメッセージやリトライを表示してくださいが、キーチェーンの外側に機密データをフォールバックとして保存することはありません。 アクセスを試みる前に、バイオメトリックの可用性をチェックするためにを使用できます。

最良のプラクティスと生産の考慮事項

  • [] ユーザまたはアイテムタイプごとに、一意で記述的なアカウント名[を使用します。
  • [] 常にアクセシビリティ属性[ を指定します。そうしないと、システムデフォルト()が適用され、これは理想的ではないかもしれません。
  • []] ユーザがログアウトした時にキーチェーンデータをクリア - 既知のアカウントに照らし、アイテムを削除。
  • []キーチェーンアクセスグループ[]のみを使用して、自分のアプリ間で共有します。 広いグループを避けてください。
  • ]キーチェーンの非感度データ(ユーザー設定のような)を格納する、代わりにまたはデータベースを使用する。
  • []]]]]]を使用して、上級シナリオ(macOS Catalyst)。
  • 実機でテスト]; シミュレータは、ハードウェアにバックアップされたストレージとは異なる動作するソフトウェアキーホルダーを使用します。

Keychain と SwiftUI と Async/Await で使う

現代のアプリでは、キーホルダーの操作を、主スレッドをブロックしないように、アクターまたは非同期セーフクラスでラップします。例は[]を使用します。

actor KeychainManager {
 func saveToken(_ token: String, for account: String) async -> Bool {
 // same implementation as above, but now it's safe to call from any context
 return saveToken(token, forAccount: account)
 }
}

生体認証を使用する場合、ユーザのやり取りを待ちながら、 [ コールはスレッドをブロックする場合があります。 背景キューでラップするか、またはそれ以上で、キーチェーンコールの前に の メソッドを使用します。

コンテンツ

The iOS Keychain is the correct place to store small, sensitive pieces of data. By using the native Keychain Services API, you gain direct control over encryption, accessibility, and authentication policies. Always pair your Keychain usage with solid error handling and remember to clear data when appropriate. For further reading, refer to the Apple Keychain Service Documentation and the Keychain Concepts overview. Adopting these practices will help you ship iOS apps that respect user privacy and withstand security scrutiny.