ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ローカル認証フローの合理化
認証にフォーカスしたLocalAuthentication APIで、ユーザデータのプライバシーやセキュリティが保護される仕組みをご確認ください。LocalAuthenticationが、App内のシークレット、キー、他の機密リソースへのアクセスを許可する仕組み、複雑さを軽減する仕組み、Touch IDやFace IDなどの一般的なローカル認証方法のセキュリティやユーザビリティに依存する仕組みについて解説します。
リソース
-
ダウンロード
♪ ♪
こんにちは Security Engineering and Architectureチームの エンジニアの Felix Aceroです このビデオでは LocalAuthenticationフレームワークを 使用して 認証機能を向上させる 方法をお伝えします Appの認証と認可の フローを改善できます まず 認証と認可の 一般的な概念を見ていきます どのように Appに適用されるかです 次に 既存の LocalAuthentication API 特にLAContextが どのように 認証スキームの実装に 役立つかを確認します そして最後に 今年LocalAuthenticationに 追加する新しいAPIが 認証コードを効率化するため どのように役立つかを 見ていきます
ではまず 認証と 認可について説明します 認証と認可は別のものですが 密接に関連する セキュリティの概念です 認証はユーザーの身元を 確認する行為です 認可とは あるユーザが 具体的なリソースに対して 特定の操作を実行できるかを 検証することです まとめると どのような リソースや操作が 利用できるかを 評価する前に まずユーザーが 本人であることを 確認する必要があるので 認証は認可を可能にします この概念を説明するために 具体的な例を見てみましょう Appで 管理される一般的な セキュリティリソースの 具体例を見てみましょう
セキュアエンクレーブキーは 特定デバイスにバインドされ メインプロセッサから 分離されたベースの キーマネージャによって 保護される特殊なキーの一種です これらのキーが特別なのは セキュアエンクレーブに 秘密鍵を保存する際 実際にキーを扱うことはなく そのキーで操作を 実行するよう指示することです セキュアエンクレーブキーは アクセス制御と関連付けできます ACLと略されます アクセス制御は 特定の操作を実行するため Blobの署名や復号化など 特定操作のため要件を指定します
例えば デバイスの ロック解除後など特定の アイテムを利用できる タイミングの指定 特定操作実行の許可のため 必要な認証要件の指定 この例では Appが キーの署名と復号の 操作を生体認証で保護し さらにキーはデバイスの ロックの解除後のみ 利用できるように したいとします では このキーに関わる 署名操作の認証フローは どのようになるか 見てみましょう
まずAppは 署名のリクエストを発行する
Appがキーに アクセスできることを確認後 システムは署名操作のための 認証要件を特定するために 処理を進めます この場合 署名の操作には 現在登録されている ユーザーから生体認証に 成功することが必要です その後 システムは 標準UIを介して 生体認証プロセスを ユーザーに説明します 認証に成功すると システムは 残りすべての認証要件が 満たされていることを 確認し 最後に署名操作を行い 署名されたBlobを Appに返します
このフローに含まれる 主な構成要素を 確認することができます まず リソースです セキュアエンクレーブキーです 次に キーで実行できる 操作をします 3番目に この要件には 操作の実行が 許可される人物と その人物の身元を 確認するために 使用されるべき認証手段が 規定されています この例のパラメータを 定義に当てはめると 認証の場合 ユーザーが正しいかどうか 生体認証で 答えることになります 一方 認可の場合は 秘密鍵を用いた署名操作が 許可されるかの問いに アクセス制御リスト 指定された要件を 確認することで回答します これがどのようなもので あるかは現在の LocalAuthenticationの APIを使って 実装することができるかを 見てみましょう LAContextが提供する機能を 簡単に確認してみましょう これはフレームワークの 中核の一つです LAContextはユーザーを 評価するために使用されます 生体認証やパスコードが 必要な時の操作を行います セキュアエンクレーブとの インターフェースにより 生体認証情報の 安全な管理も可能です この観点から LAContextは認証ケースを サポートするために 使用することができます LAContext は 他のフレームワークと連携し 認可フローを サポートすることもできます 例えば アクセス コントロールリストの 評価に 使用することができます もっと詳しく見てみましょう まず 秘密鍵に関連する ACLにアクセスする 必要があります 安全なフレームワークが 提供する SecItemCopyMatching APIの 助けを借りて行えます クエリ内でReturnAttributes キーの提供を確認してください
アクセス制御リストが アクセス権を取得すると LAContextとevaluate AccessControl APIで 直接評価することが できるようになります この方法の最大の利点は ユーザーに認証を求める タイミングと場所を App内で 決められることです この場合 アクセス制御リストが 署名操作のために 生体認証を要求しているので LAContextは FaceID または TouchIDのUIを表示します
ACLが LAContext内で 認可されると キーへの参照を得るため クエリの一部として 使用できるようになります これは LAContextを UseAuthenticationContext キーの下で SecItemクエリに追加します
LAContextを秘密鍵の 参照にバインドすることで 署名の操作を実行しても 別の認証の発生は保証し 不要なプロンプトを出さずに 操作を継続できます これらのバインディングは LAContextが無効になるまで 今後の署名に追加ユーザー インタラクションは必要ない
LAContextは 非常に柔軟性が高く 認可フローに関わる 各ステップやパラメータを 制御することができます また セキュリティ フレームワークなど 他と組合せての使用も可能で 幅広いユースケースに対応します この汎用性の代償として コードがより複雑になります 複数フレームワークが 提供するAPIを注意深く調和 特に キー シークレット コンテキスト アクセス制御リストへの 低レベルのアクセスが 必要とする Appの場合 LAContextは 最適なツールになります しかし コンテンツや 機密性の高いリソースへの アクセスを許可する 方法だけが必要な場合は この柔軟性と引き換えに よりシンプルなAPIを 使用することができます 最後のトピックは Appの効率化です iOS16とmacOS13の新機能で LocalAuthenticationが導入されます より高度な 認証に特化したAPIです 新しいAPIは LocalAuthenticationの 既存の概念の上に構築され 一般的な認証フローの実装を 簡素化することを目的と しています Appの中核に エネルギーを集中させます 新しいAPIで導入された 重要な抽象化はLARightです
LARightの 最も単純な使用例は Appで 定義されたリソースの承認 例えばAppの 「ユーザープロファイル」 セクションへの アクセスを制限するために まずユーザーが生体認証に 成功することが必要です
デフォルトでは 使用するデバイスに応じて TouchID FaceID Apple Watch またはパスコードを使い 認証できる 一連の認証要件によって 権利が保護されます
また 自分の権利と 関連付ける より詳細な要件が 必要です 認証手段をさらに 制限することができます それでは LARightsの 使い方を見てみましょう
まず最初にするべきことは 権利のインスタンス化です 必要条件を 指定して実行します この場合 ログイン権には バイオメトリクスの使用 またはデバイスの パスコードの入力が必要です 次にユーザーがログイン権を 取得できることを確認します この情報をもとに ログイン操作を継続できるか Appの公開セクションに リダイレクトを判断するため 使用されます 最後に 実際のアクセス 認可に進むことができます ローカライズされた理由を 提供することで ユーザーに見えるように なります
このように権利を認可すると 全く新しいシステム ドリブンなUIが表示されます UIはAppの ウィンドウ内に レンダリングされ 操作の由来や目的を 理解してもらうために 関連情報を提供します この新しいデザインにより よりシームレスに統合された 承認フローを作成することが 可能になると確信しています それはユーザーにより多くの 背景と情報を提供します
ここまで権利の作成と認可の 方法について見てきました では そのライフサイクルを 詳しく見てみましょう 権利のライフサイクルは 未知の状態です Appが許可の リクエストを発行すると 同時に権利の状態が 認可に変更します この時点で ユーザーには 前のスライドで 見た認可のUIが 表示されます
操作の成否により 権利の状態は 「許可する」または 「許可しない」に遷移する Appにとって 最も重要な状態遷移です 最後に 権利は 「許可する」状態から 「許可しない」状態へも 移行できます Appが権利の 認証解除要求を発行したとき インスタンスが割り当て 解除されたときに発生します
認可された状態を 維持するために 必ず権利の参照を 強くしてください
権利の認可が解除された後 Appは 認可要求を発行して サイクルを 再開することができます LARightインスタンスへの アクセス権を持っている場合 その状態のプロパティを 直接問い合わせられます KVOやCombineを使い 状態遷移の観察できます また Appで 複数の権利を扱います デフォルトの NotificationCenterに 発行される didBecomeAuthorizedと didBecomeUnauthorizedの 通知があり 権利の状態を一箇所で 観察することができます 認証状態の変化が 検出された後に発行されます
次に進む前に この例題に戻りましょう ログインの認証を 解除してみましょう こうすることで 次回のログイン時に 新たな認証が必要なことを 保証しています
権利のインスタンスを使い 操作の認可方法を見ました Appで定義された リソースの操作を許可します また これらの権利の ライフサイクルと状態が 最終的にランタイムと どう結びついているかを 見てきました つまり Appの すべてのセッションで権利を 正しく設定する必要がある どのように権利を 持続させることができるか どのような可能性が あるかを見ていきましょう
LARightsは RightStoreを 利用して 永続化できます
永続化される場合 権利は アクセス制御リスト(ACL)で 保護されたセキュアエンクレーブ キーでバックアップされます 権限要件に一致するアクセス 制御リストまたはACで保護 この方法は 権利が永続化された後も 認可要件が不変であることを 保証することに役立ちます
また権利をバックアップする 秘密鍵にアクセスできます それを使って復号 署名 キー交換などの 保護された 暗号化操作を行う
対応する公開キーにも アクセス可能です 暗号化 署名検証などの 操作を行うことができます これは公開キーなので 関連するバイトを
秘密鍵の操作のみ可能で 権利の認証成功後に 許可されます これに対して公開鍵の操作は 常に許可されています
権利を永続化するとき 単一の不変の秘密も 一緒に保存する機会が あります また秘密は権利の認可要件に 合致するアクセス制御 リストと関連付けられ 権利が認可された後にのみ アクセス可能になります
要約すると LAPersistedRightsは RightStoreの 支援によって作成される 一度だけ設定されます その認証要件は不変です これらは保存されるため Appの異なる セッションにまたがって 使用することができます 内部的には特定の デバイスにバインドされ 一意のセキュアエンクレーブ キーに支えられ そのキーを使用して 権利の認証状態に応じて 異なる暗号化操作を実行する ため使用することができます 最後に これらの技術は 単一の不変の秘密を 保護するために 使用することができます 権利に関連する機能を 理解したところで プレゼンテーションの 冒頭で説明した 署名操作を許可する シナリオの実装に どのように役立つかを 見てみましょう 正規権利をインスタンス化し 認可要件の指定から始める この場合 権利が発生した時点で 生体認証が登録されている ユーザーに対してのみ 権利が発生するように したいと思います そこで バイオメトリックス カレントセットの要件を利用
Right Storeから 一意な識別子を提供する 権利を永続化することが できます この識別子はAppの 将来のセッションで権利を 取得する必要があるときに 役に立つことでしょう
権利が永続化されると 公開鍵にすぐにアクセスでき 明示的な認証を必要とせずに 保護されていない操作を 実行できるようになります この例では 単純に公開 バイトをエクスポートします
この後 署名操作を行うとき ストアから権利を 取得することができます 作成時に指定した一意の 識別子でを使っています 次に 現在のユーザーを 認証することができます 権利の認可操作を 実行します この時点で システムは 認証プロセスを実行します 要件が満たされていることを 確認します
権利の認証が完了したら その秘密鍵を使用できます 保護された暗号化操作を 行うために この場合Appの サーバーが発行する チャレンジに署名するために 秘密鍵を使用します 最後に 認証と認可の 一般的な概念の間に 存在する関係 特に認証が認可を どのように可能にするかを お話しました LAContextが提供する 機能の一部と安全な フレームワークがどのように 組み合わせるか説明しました 強力で拡張性の高い 認証フローを実現しました そして最後に 新しく 追加されたLARightが 特定の認可のユースケースを 実装するために どのようにコードの効率化を 図れるかを調べました AppでLocal Authenticationの使用時に 説明したいくつかの機能が ユーザーのプライバシーと セキュリティを保護しながら コードを簡素化することに 役立つかを御検討ください ありがとうございました
-
-
4:58 - LAContext (authorize a signature operation 1)
let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecAttrApplicationTag as String: "com.example.app.key", kSecReturnAttributes as String: true, ] var item: CFTypeRef? = nil guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, let attrs = item as? NSDictionary, let accessControl = attrs[kSecAttrAccessControl] else { throw .aclNotFound }
-
5:15 - LAContext (authorize a signature operation 2)
let context = LAContext() try await context.evaluateAccessControl(accessControl as! SecAccessControl, operation: .useKeySign, localizedReason: "Authentication is required to proceed")
-
5:44 - LAContext (authorize a signature operation 3)
let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecAttrApplicationTag as String: "com.example.app.key", kSecReturnRef as String: true, kSecUseAuthenticationContext as String: context ] var item: CFTypeRef? = nil guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, item != nil else { throw .keyNotFound }
-
6:00 - LAContext (authorize a signature operation 4)
let privateKey = item as! SecKey var error: Unmanaged<CFError>? guard let sgt = SecKeyCreateSignature(privateKey, self.algorithm, blob, &error) as Data? else { throw .signatureFailure }
-
8:28 - LA Right (basic usage)
// LARight: Basic usage func login() async { self.loginRight = LARight(requirement: .biometry(fallback: .devicePasscode)) do { try await loginRight.checkCanAuthorize() } catch { navigateTo(section: .public) return } do { try await self.loginRight.authorize(localizedReason: self.localizedReason) navigateTo(section: .protected) } catch { showError(.authenticationRequired) } }
-
11:01 - LARight (logout and deauthorization)
// LARight: Basic usage func login() async { self.loginRight = LARight(requirement: .biometry(fallback: .devicePasscode)) // ... do { try await self.loginRight.authorize(localizedReason: self.localizedReason) navigateTo(section: .protected) } catch { showError(.authenticationRequired) } } func logout() async { await self.loginRight.deauthorize() }
-
13:44 - LAPersistedRight
// LAPersistedRight: Retrieval and private key usage func generateClientKeys() async throws -> Data { let login2FA = LARight(requirement: .biometryCurrentSet) let persisted2FA = try await LARightStore.shared.saveRight(login2FA, identifier: "2fa") return try await persisted2FA.key.publicKey.bytes } func signChallenge(_ challenge: Data, algorithm: SecKeyAlgorithm) async throws -> Data { let persisted2FA = try await LARightStore.shared.right(forIdentifier: "2fa") let localizedReason = "Biometric authentication is required to proceed" try await persisted2FA.authorize(localizedReason: localizedReason) return try await persisted2FA.key.sign(challenge, algorithm: algorithm) }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。