Keychainへの秘密情報の保存
認証情報を安全に保存・取得します。
「Keychainへの秘密情報の保存」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。
Keychainを使う理由
パスワード、トークン、キーをUserDefaultsや平文ファイルに保存してはいけません。これらは簡単に読み取られてしまいます。Keychainは、ハードウェアとユーザーのパスコードによって保護された、暗号化済みでOSが管理する小さな秘密情報用のデータベースです。認証情報を保存する唯一の適切な場所です。
import Security
// Keychain stores secrets encrypted at rest,
// survives app updates, and gates access by policy.アイテムは辞書で表す
Keychain Services APIはCベースです。kSec...定数のキーを使った[String: Any]クエリ辞書で、アイテムを指定します。同じ辞書形式を、追加、検索、更新、削除で使い回します。
import Security
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "user@example.com",
kSecAttrService as String: "com.example.app"
]
_ = queryアイテムクラス
kSecClassキーでアイテムの種類を選択します。kSecClassGenericPasswordはアプリのトークンや秘密情報に、kSecClassInternetPasswordはホストやプロトコルの属性を持つサーバー認証情報に使用します。ほとんどのアプリの秘密情報には、汎用パスワードを使用します。
import Security
// kSecClassGenericPassword -> tokens, API keys
// kSecClassInternetPassword -> server logins
// kSecClassKey / Certificate -> crypto material
let cls = kSecClassGenericPassword
_ = clsアイテムの追加
SecItemAddは新しいアイテムを追加します。kSecValueDataの下にDataとしてデータを指定し、アイテムを識別する属性も追加します。戻り値はOSStatusで、errSecSuccessは処理が成功したことを示します。
import Security
func save(_ token: String, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: Data(token.utf8)
]
return SecItemAdd(query as CFDictionary, nil)
== errSecSuccess
}重複の処理
識別属性がすでに存在するアイテムを追加すると、errSecDuplicateItemが返されます。堅牢な保存処理では、まずSecItemAddを試し、重複した場合はSecItemUpdateにフォールバックします。これはアップサートパターンです。
import Security
func upsert(_ data: Data, account: String) -> Bool {
let base: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
var add = base
add[kSecValueData as String] = data
let status = SecItemAdd(add as CFDictionary, nil)
if status == errSecDuplicateItem {
return SecItemUpdate(base as CFDictionary,
[kSecValueData as String: data] as CFDictionary)
== errSecSuccess
}
return status == errSecSuccess
}アイテムの読み取り
SecItemCopyMatchingで検索します。シークレットのバイト列を取得するには、kSecReturnDataをtrueに設定し、kSecMatchLimitをkSecMatchLimitOneに設定する必要があります。結果はoutパラメーターを通じてCFTypeRefとして返されます。
import Security
func load(account: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var result: CFTypeRef?
let status = SecItemCopyMatching(
query as CFDictionary, &result)
guard status == errSecSuccess else { return nil }
return result as? Data
}アイテムの更新
SecItemUpdateは2つの辞書を受け取ります。1つはアイテムを検索するクエリ、もう1つは更新する属性の辞書です。変更されるのは指定した属性だけで、それ以外はすべて保持されます。
import Security
func update(_ newData: Data, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let attrs: [String: Any] = [
kSecValueData as String: newData]
return SecItemUpdate(query as CFDictionary,
attrs as CFDictionary) == errSecSuccess
}アイテムの削除
SecItemDeleteは一致するアイテムを削除します。存在しないものを削除するとerrSecItemNotFoundが返されます。ログアウト時に認証情報を消去する場合は、これを成功として扱えます。
import Security
func delete(account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
|| status == errSecItemNotFound
}OSStatusのデコード
エラーは整数のOSStatusコードです。SecCopyErrorMessageStringを使うと、人間が読める説明に変換できます。保存や読み取りが失敗した理由をデバッグする際に非常に役立ちます。
import Security
func describe(_ status: OSStatus) -> String {
return SecCopyErrorMessageString(status, nil)
as String? ?? "OSStatus \(status)"
}アイテムの一意な識別
アイテムは属性の組み合わせによって照合されます。一般的なパスワードでは、通常kSecAttrServiceとkSecAttrAccountを組み合わせます。異なるシークレットが衝突しないよう、安定したアプリ固有のサービス文字列を選んでください。
import Security
// Uniqueness for generic passwords usually comes from:
// service (your bundle id) + account (the username)
let service = "com.example.app.auth"
let account = "current-user"
_ = (service, account)小さなラッパー
生のAPIは冗長なため、チームではsave、read、deleteを公開する小さな型でラップします。これにより、kSec関連の定型処理を1か所に集約でき、呼び出し側もすっきりします。
import Security
struct TokenStore {
let service = "com.example.app.auth"
func read(_ account: String) -> Data? {
let q: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var out: CFTypeRef?
return SecItemCopyMatching(q as CFDictionary, &out)
== errSecSuccess ? out as? Data : nil
}
}クイックチェック
保存したシークレットのバイト列を正しく取得する方法を思い出してください。
まとめ
KeychainのCRUDについて学びました。
- 認証情報は暗号化されたKeychainに保存し、UserDefaultsやファイルには決して保存しないでください。
kSecのクエリ辞書でアイテムを記述し、kSecClassGenericPasswordのようなクラスを選択します。SecItemAdd/SecItemCopyMatching/SecItemUpdate/SecItemDeleteでライフサイクルを扱います。errSecDuplicateItemにはupsertで対応します。- サービスとアカウントでアイテムを識別し、デバッグのために
OSStatusをデコードします。
よくある質問
「Keychainへの秘密情報の保存」レッスンは無料ですか?
はい。「Keychainへの秘密情報の保存」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。
「Keychainへの秘密情報の保存」で何を学びますか?
認証情報を安全に保存・取得します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Swift Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「Keychainへの秘密情報の保存」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このSwift Academyレッスンでコードを書いて実行できますか?
はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Keychainへの秘密情報の保存
- Keychainのアクセス制御
- 生体認証
- データ保護と暗号化