エントリー
iOS 8以降、AppleはサードパーティアプリケーションでTouch IDテクノロジー(iPhone 5sに組み込まれた指紋スキャナーを使用した認証)を使用する可能性へのアクセスを開きます。 この点で、開発者が正確に利用できるようになったもの、アプリケーションに埋め込む方法、動作を詳しく説明し、最も実装しやすい便利な「ラッパー」を共有したいと思います。 Touch IDのユースケースと思われます。
必要なAPIは、新しいLocalAuthenticationフレームワークに導入されています。 現時点では、その機能は指紋スキャナーとの対話に限定されていますが、より一般的な名前で判断すると、その機能セットは将来拡張される可能性があります。 フレームワークはユーザーに関するデータを提供しません(これは一般に論理的です)が、ユーザーに生体認証を使用した認証のみを提供できます(現時点では組み込みの指紋スキャナーです;具体的には、フレームワーク内のスキャナーについての話はありません、より一般的なものが使用されます) word Biometrics)。 出力で、ステータスを取得します。認証が成功したか、何かがおかしかったです。 実際、ほぼいつでも、デバイスを使用する人が所有者であるかどうかを判断できます。
これは、重要な操作を実行するときに、追加の保護としてTouch IDを使用することをお勧めします。 たとえば、資金の送金を確認するとき、重要な設定を変更するとき、セキュアチャットを初期化するときなど、スマートフォンが攻撃者の手に渡っていないことをアプリケーションができる限り確認する必要があります。
投稿を読みやすくするだけでなく再利用できるようにするために、Touch IDとの統合を、上記のシナリオを実装する「ラッパー」の形で説明することにしました。これにより、今後数時間の作業時間を節約できます。 説明は「タスクソリューション」の形式で示されているため、何が行われているのか、またその理由は明らかです。 それでは、始めましょう。
挑戦する
アプリケーションで重要な操作を実行する場合、組み込みの生体認証を使用してユーザー認証を要求できる必要があります。 このような認証を要求する必要性は、ユーザーがカスタマイズできる必要があります。 また、アプリケーションは、以前のバージョンのオペレーティングシステムと、生体認証を持たないデバイスで実行できることにも留意してください。
解決策
ソリューションは、BiometricAuthenticationFacadeクラスで提示されます。
まず最初に、最も重要なこと-LocalAuthenticationフレームワークとの相互作用を検討してください。 この部分はユーザーには表示されず、クラスインターフェイスからはアクセスできません。
クラス拡張で、コンテキストを保存するプロパティを宣言します。
@interface BiometricAuthenticationFacade () @property (nonatomic, strong) LAContext *authenticationContext; @end
APIの可用性を考慮して、プロパティを初期化します。
- (instancetype)init { self = [super init]; if (self) { if (self.isIOS8AndLater) { self.authenticationContext = [[LAContext alloc] init]; } } return self; }
次に、ローカル認証の使用の可用性を返すメソッドを定義します。
- (BOOL)isPassByBiometricsAvailable { return [self.authenticationContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:NULL]; }
パラメータとして、
canEvaluatePolicy:error:
メソッドはローカル認証のタイプを受け入れます。 現在、
LAPolicyDeviceOwnerAuthenticationWithBiometrics
1つのタイプのみ
LAPolicyDeviceOwnerAuthenticationWithBiometrics
おり、それはそれ自身を表しています。 デバイスがこの機能を物理的にサポートしていない場合、またはユーザーがスマートフォンの設定でこの機能を有効にしていない場合、生体認証の使用は利用できない場合があります。
ユーザー指紋スキャンを実行する要求は、次のとおりです。
- (void)passByBiometricsWithReason:(NSString *)reason succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { [self.authenticationContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:reason reply:^(BOOL success, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (success) { successBlock(); } else { failureBlock(error); } }); }]; }
パラメーターとして、
evaluatePolicy:localizedReason:reply:
メソッドは、上記のタイプのローカル認証、要求の理由を簡潔に説明するメッセージ、および手順全体の完了後に非同期に実行されるブロックを受け入れます。
メインスレッドでの
reply
ブロックの実行は保証されていないことに注意してください(実際、メインスレッドでは呼び出されません)。そのため、
dispatch_async
呼び出しが追加されています。 そのままにしておくこともできますが、ほとんどの開発者は、メインスレッドで呼び出されるメソッドに渡されるブロックがメインスレッドでも呼び出されると想定し、追加のチェックは行いません。 それはとても歴史的に起こりました。
上記のメソッドを呼び出すと、ダイアログが表示されます。
- タイトルにはアプリケーションの名前(
CFBundleDisplayName
)が使用されます。 -
localizedReason
パラメーターとして指定されたストリング。 - このフィールドではすべてがそれほど単純ではありません。 クリックすると、パスワードを入力するためのダイアログが表示されることはありませんが、代わりにエラーで
reply
ブロックが呼び出されます。 エラーコードは文書化されています:
LAErrorUserFallback
つまり、それは考案されました。 ユーザーFlanker_4が説明したように、システムはアプリケーションに代替認証、つまりアプリケーションパスワードの入力を個別に実行するよう促します。 つまり、パスワードの入力と検証のためのダイアログとロジックの実装は開発者にあります。
ユーザーがフォールバックボタン(パスワードの入力)をタップしたため、認証がキャンセルされました。
- リクエストをキャンセルするボタン。 その結果、対応する
LAErrorUserCancel
エラーでreply
ブロックが呼び出されます。
スキャンが成功すると、
reply
ブロックが呼び出され、肯定的な結果が返されます。
evaluatePolicy:localizedReason:reply:
メソッドが呼び出されるたびに、スキャンのダイアログが表示されるわけではないことに注意してください。 つまり、最後のスキャンの成功には寿命があります。 数分以内に認証を再試行すると、すぐに
reply
ブロックが呼び出され、肯定的な結果が得られます。
間違った指を使用し、5回連続でスキャンしようとすると、スマートフォンの設定で指定されたパスワードを入力するように求められます。
明確にするために、パスワードを作成せずにスマートフォンの設定でスキャナーをオンにすることは不可能であることを明確にします。
ユーザーが正しいパスワードを入力すると、再び指紋スキャンが提供されます。 しかし、 simplixユーザーが指摘したように、デバイスのパスワードがわかっている場合は、グローバルデバイス設定でスキャナーを無効にできます。 実際、スキャンのためにダイアログを再表示しても意味がありません。
これで
LocalAuthentication
との相互作用が
LocalAuthentication
ます。
ファサードのインターフェースの実装に移りましょう。
認証の可用性を確認する方法。 結果は、APIとスキャナーの可用性によって決まります。
- (BOOL)isAuthenticationAvailable { return self.isIOS8AndLater && self.isPassByBiometricsAvailable; }
特定の操作で認証が有効かどうかを判断する方法:
- (BOOL)isAuthenticationEnabledForFeature:(NSString *)featureName { return self.isAuthenticationAvailable && [self loadIsAuthenticationEnabledForFeature:featureName]; }
操作の例としては、設定へのアクセス、金銭取引の実行などがあります。
包含ステータスはNSUserDefaultsに保存されます。
loadIsAuthenticationEnabledForFeature:
メソッドの実装を
loadIsAuthenticationEnabledForFeature:
に示し
loadIsAuthenticationEnabledForFeature:
。
特定の操作の認証を有効にする方法:
- (void)enableAuthenticationForFeature:(NSString *)featureName succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { if (self.isAuthenticationAvailable) { if ([self isAuthenticationEnabledForFeature:featureName]) { successBlock(); } else { [self saveIsAuthenticationEnabled:YES forFeature:featureName]; successBlock(); } } else { failureBlock(self.authenticationUnavailabilityError); } }
このメソッドは、アプリケーションのユーザーが追加の検証が必要な操作を独立して決定できるようにするために必要です。
イネーブル状態は
NSUserDefaults
保存され
NSUserDefaults
。
saveIsAuthenticationEnabled:forFeature
メソッドの実装を
saveIsAuthenticationEnabled:forFeature
に示し
saveIsAuthenticationEnabled:forFeature
。
特定の操作の認証無効化方法:
- (void)disableAuthenticationForFeature:(NSString *)featureName withReason:(NSString *)reason succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { if (self.isAuthenticationAvailable) { if ([self isAuthenticationEnabledForFeature:featureName]) { [self passByBiometricsWithReason:reason succesBlock:^{ [self saveIsAuthenticationEnabled:NO forFeature:featureName]; successBlock(); } failureBlock:failureBlock]; } else { successBlock(); } } else { failureBlock(self.authenticationUnavailabilityError); } }
ご覧のとおり、オフにするには、攻撃者ではなくスマートフォンの所有者と取引していることを確認する必要があります。
操作にアクセスするためのユーザー認証要求メソッド:
- (void)authenticateForAccessToFeature:(NSString *)featureName withReason:(NSString *)reason succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { if (self.isAuthenticationAvailable) { if ([self isAuthenticationEnabledForFeature:featureName]) { [self passByBiometricsWithReason:reason succesBlock:successBlock failureBlock:failureBlock]; } else { successBlock(); } } else { failureBlock(self.authenticationUnavailabilityError); } }
操作にアクセスするためのユーザー認証の必要性に関する情報を保存および取得するためのメソッド(クラスインターフェイスからは利用不可):
- (void)saveIsAuthenticationEnabled:(BOOL)isAuthenticationEnabled forFeature:(NSString *)featureName { NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSMutableDictionary *featuresDictionary = nil; NSDictionary *currentDictionary = [userDefaults valueForKey:kFeaturesDictionaryKey]; if (currentDictionary == nil) { featuresDictionary = [NSMutableDictionary dictionary]; } else { featuresDictionary = [NSMutableDictionary dictionaryWithDictionary:currentDictionary]; } [featuresDictionary setValue:@(isAuthenticationEnabled) forKey:featureName]; [userDefaults setValue:featuresDictionary forKey:kFeaturesDictionaryKey]; [userDefaults synchronize]; } - (BOOL)loadIsAuthenticationEnabledForFeature:(NSString *)featureName { NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSDictionary *featuresDictionary = [userDefaults valueForKey:kFeaturesDictionaryKey]; return [[featuresDictionary valueForKey:featureName] boolValue]; }
NSUserDefaults
ストレージとして使用されます。 キーの名前が競合する可能性を減らすために、すべてのデータは別の辞書に保存されます。
これで、ファサードの主要な実装が完了しました。
終了
最後に、最後まで読み終えた人のために、iPhone 5sのスキャナーに関するいくつかの興味深い事実を紹介します。
- 偽合格の確率、すなわち ランダムな人の指紋があなたのものとして認識されるという事実は、50,000人に1人です。
- システムでは、ユーザーパスワードが要求される前に5回のスキャン試行を実行できます。 したがって、ブルートフォース攻撃は実行できず、スキャナーが攻撃者によってハッキングされる可能性は約0.0001です。
- スキャナーは、88x88ピクセルのサイズと500 ppiの密度のラスターイメージをキャプチャします。 結果のラスターイメージはベクトルに変換され、追加の分析が行われます。
- 受信した指紋データは、A7プロセッサの特別な領域(Secure Enclave)に暗号化された形式で保存されます。 データは秘密鍵で暗号化されます。秘密鍵は、工場でのプロセッサの生産中に生成され、Secure Enclaveに書き込まれます。 Appleは、暗号化されたデータも秘密鍵もモバイルデバイスを離れず、Apple自身を含む第三者には知られていないと主張しています。
興味深い事実のソース: iOSセキュリティ
GitHubで利用可能な完全なソースコード: BiometricAuthenticationFacade