🔒
機密情報と非機密情報の保存方法の違い
概要
iOSアプリにおいて、非機密情報と機密情報を分けて管理するために、UserDefaultsとKeychainという2つの仕組みを使い分ける。ここでは、それぞれの特徴と使用例について扱う。
UserDefaults
特徴
項目 | 内容 |
---|---|
用途 | ユーザーの設定やアプリの状態を保存するために使われる。 |
保存場所 | アプリのサンドボックス内のファイルシステムに保存される。 |
セキュリティ | データは暗号化されず、アプリのサンドボックス内で保護される。機密情報の保存には向かない。 |
アクセス | 簡単にアクセスできる。 |
適しているデータ
- アプリの設定(テーマモード、通知のオン/オフ)
- 最終ログイン日時やアプリのバージョン情報
- ユーザーの非機密な状態情報(アプリを初めて起動したときにチュートリアルを表示するための一時的なフラグ)
1. 保存、取得
let defaults = UserDefaults.standard
// 初回起動時のフラグを取得
if !defaults.bool(forKey: "hasSeenTutorial") {
print("チュートリアルを表示")
// チュートリアルを表示後、フラグを保存
defaults.set(true, forKey: "hasSeenTutorial")
} else {
print("チュートリアルは既に表示済み")
}
2. 更新
let defaults = UserDefaults.standard
// フラグをリセット
defaults.set(false, forKey: "hasSeenTutorial")
print("フラグを未表示に更新")
3. 削除
let defaults = UserDefaults.standard
// フラグを削除
defaults.removeObject(forKey: "hasSeenTutorial")
print("フラグを削除。")
Keychain
特徴
項目 | 内容 |
---|---|
用途 | 機密性の高い情報を安全に保存するための仕組み。 |
保存場所 | データは暗号化されてiOSシステムが管理するKeychainに保存される。 |
セキュリティ | AppleのSecurityフレームワークを使用しており、データは暗号化される。 |
アクセス | UserDefaultsよりもやや複雑。 |
適しているデータ
- パスワード
- 認証トークン
- 機密性の高いAPIキー
1. 保存
クエリのキー
kSecClass
kSecAttrAccount
kSecAttrService
kSecValueData
1.1 kSecClass(アイテムの種類を指定)
役割:Keychainに保存するデータの種類(クラス)を指定する。
例:保存するデータが「一般的なパスワード」なのか、「証明書」なのかを指定する。
値 | 簡単な説明 | どんなときに使うか | 具体例 |
---|---|---|---|
kSecClassGenericPassword |
アプリで使うパスワードや設定を保存するためのもの | アプリのログイン情報やAPIキーを保存したいとき | アプリの「ID」と「パスワード」、設定データ |
kSecClassInternetPassword |
インターネットのログイン情報を保存するためのもの | Webサービスやオンラインのアカウント情報を保存したいとき | GmailやTwitterのログイン情報 |
kSecClassCertificate |
安全な通信のための「証明書」を保存するもの | データ通信を安全にするための情報を使うとき | サーバーやアプリの信頼性を証明するデータ |
kSecClassKey |
暗号化やデータ保護に必要な「鍵」を保存するもの | データを暗号化したり、デジタル署名を使いたいとき | 安全な通信をするための秘密鍵や公開鍵 |
kSecClassIdentity |
証明書と秘密鍵のペアを保存するもの | サインや認証に使う証明書と鍵を一緒に管理したいとき | Webサービスでユーザーの身分を確認するための証明書と秘密鍵 |
1.2 kSecAttrAccount(アカウント名を指定)
役割: 保存するデータに関連付けるアカウント名(例: ユーザー名やメールアドレス)を指定する。
例: アカウントを特定するために使用。
値 | 簡単な説明 | どんなときに使うか | 具体例 |
---|---|---|---|
文字列 | 保存するアイテムのアカウント名 | 保存するアイテムをアカウントごとに管理したいとき |
user@example.com など |
1.3 kSecAttrService(サービス名を指定)
役割: 保存するデータを特定のサービスに関連付けるための識別子を指定する。
例: アプリやサービスを識別するために使用。
値 | 簡単な説明 | どんなときに使うか | 具体例 |
---|---|---|---|
文字列 | 保存先のサービス名を指定 | アプリや特定のサービス名に関連付けてデータを保存したいとき |
com.example.myapp など |
1.4 kSecValueData(保存するデータそのもの)
役割: 実際に保存するデータ(例: パスワードやAPIキー)を指定する。
例: ユーザーのパスワードや認証情報を保存する。
値 | 簡単な説明 | どんなときに使うか | 具体例 |
---|---|---|---|
Data 型の値 |
保存するデータそのもの | ユーザーのパスワードやAPIキーをKeychainに保存したいとき |
password123 など(データ型で) |
import Security
func savePasswordForAccount(account: String, service: String, password: String) -> Bool {
let passwordData = password.data(using: .utf8)! // パスワードをデータ型に変換
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecAttrService as String: service,
kSecValueData as String: passwordData
]
SecItemDelete(query as CFDictionary) // 既存データを削除
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
// 使用例
if savePasswordForAccount(account: yourEmail, service: "com.example.myapp", password: enteredPassword) {
print("Keychainに保存しました!")
} else {
print("保存に失敗しました。")
}
2. 取得
クエリのキー
kSecClass
kSecAttrAccount
kSecAttrService
kSecReturnData
2.1 kSecReturnData(データ部分を返すか指定
役割: 保存されている値(パスワードなど)を取得するかを指定する。
例: 実際のパスワードデータを取得したい場合に使用。
値 | 簡単な説明 | どんなときに使うか | 具体例 |
---|---|---|---|
真偽値 | データを返す設定 | パスワードやAPIキーを取得したいとき | true |
※kSecReturnDataをfalseにするのは、データ本体が不要で、存在確認やアカウント名・サービス名などの属性情報だけを取得したい場合など。
func getPasswordForAccount(account: String, service: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecAttrService as String: service,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &item)
if status == errSecSuccess, let data = item as? Data {
return String(data: data, encoding: .utf8)
}
return nil
}
// 使用例
if let password = getPasswordForAccount(account: "user@example.com", service: "com.example.myapp") {
print("取得したパスワード: \(password)")
} else {
print("パスワードが見つかりませんでした。")
}
3. 更新
クエリのキー
kSecClass
kSecAttrAccount
kSecAttrService
func updatePasswordForAccount(account: String, service: String, newPassword: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecAttrService as String: service
]
let updateFields: [String: Any] = [
kSecValueData as String: newPassword.data(using: .utf8)!
]
let status = SecItemUpdate(query as CFDictionary, updateFields as CFDictionary)
return status == errSecSuccess
}
// 使用例
if updatePasswordForAccount(account: yourEmail, service: "com.example.myapp", newPassword: newPassword) {
print("パスワードを更新しました!")
} else {
print("更新に失敗しました。")
}
4. 削除
クエリのキー
kSecClass
kSecAttrAccount
kSecAttrService
func deletePasswordForAccount(account: String, service: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecAttrService as String: service
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
// 使用例
if deletePasswordForAccount(account: yourEmail, service: "com.example.myapp") {
print("Keychainデータを削除しました!")
} else {
print("削除に失敗しました。")
}
まとめ
UserDefaultsは非機密情報の保存に適し、Keychainはパスワードやトークンなど機密情報を安全に保存するために使用される。
Discussion