生体認証とセキュアエンクレーブ
指紋や顔で守られた鍵はなぜ抜き取れないのか。Secure EnclaveとKeychain/Keystoreの保護原理を押さえ、破られにくい認証を自分で設計できる。
- 生体データはSecure Enclave/TEEという分離された実行環境の中だけで照合され、生の指紋・顔データはアプリにもOS本体にも渡らない。
- Keychain/Keystoreの鍵は「非エクスポート性」を持ち、鍵そのものは外に出せず、暗号化・署名の操作をエンクレーブに依頼する形でしか使えない。
- 生体認証は本人確認そのものではなく、鍵アクセスのアクセス制御条件として使われる。認証成功が鍵の解錠フラグを立て、その結果として暗号操作が許可される。
生体認証は「本人確認」ではなく「鍵の解錠条件」である
Face IDやTouch ID、Androidの指紋認証を「サーバーに顔や指紋を送って本人か確かめる仕組み」と捉えると、設計を誤ります。モバイルの生体認証が実際にやっているのは、端末内に保管された鍵へのアクセスを、生体照合の成否というローカルなゲートで制御することです。照合はネットワークを介さず端末内で完結し、生の生体データは外に一切出ません。ここを起点に、なぜ鍵が抜き取れないのか、生体認証と鍵アクセスがどう結び付いているのかを内部動作から見ていきます。
Secure EnclaveとTEE:メインOSから隔離された実行環境
鍵と生体照合を守る土台は、メインのアプリケーションプロセッサ(AP)とは分離された安全な実行環境です。AppleはこれをSecure Enclave(専用コプロセッサ)として実装し、Androidの多くの端末はArmのTrustZoneが提供するTEE(Trusted Execution Environment、信頼された実行環境)や、独立した専用チップ(GoogleのTitan M系など)として実装します。
| 観点 | 通常のOS(リッチ側) | Secure Enclave/TEE(セキュア側) |
|---|---|---|
| 実行するコード | アプリ・OSカーネル・ドライバ | 検証済みの小さなセキュアOS/ファームウェアのみ |
| メモリ | 共有のシステムメモリ | 暗号化された専用領域。リッチ側から直接読めない |
| 生体データ | 触れられない | 照合テンプレートをこの中だけで保持・比較 |
| 鍵素材 | ハンドル(参照)だけを持つ | 鍵の実体を保持し操作を代行する |
TrustZoneはCPUを「セキュアワールド」と「ノンセキュアワールド」に時分割で切り替え、セキュアワールドでのみアクセスできるメモリ・周辺機器を物理的に区切ります。Secure Enclaveはさらに徹底しており、独自のブートROM・暗号化されたメモリ・AESエンジンを持つ独立したコプロセッサとして、メインCPUがバグや脆弱性で乗っ取られても内部状態に到達できない設計です。要点は、機密資産(生体テンプレートと鍵)をリッチ側のソフトウェアが原理的に触れない場所へ隔離していることにあります。この隔離思想はモバイル特有ではなく、/embedded/ で扱うハードウェア分離や信頼の起点(Root of Trust)の考え方と地続きです。
生体テンプレートはどこへも出ない
指紋センサーや顔認識カメラが取得した生データは、リッチ側のOSを経由せず、セキュア側へ直接(またはセッション鍵で暗号化した経路で)渡されます。セキュア側は初回登録時に生データから数理的な特徴量(テンプレート)を抽出して暗号化保存し、認証時にはその場でセンサー入力とテンプレートを比較して、返すのは一致/不一致という真偽値だけです。
Face IDやTouch IDの照合結果としてアプリやOSに渡るのは「認証に成功したか」という結果のみで、顔画像・指紋画像・テンプレートそのものは決して境界の外へ出ません。したがって開発者が生体データを直接扱うAPIは存在せず、扱えないことがそのままプライバシー保護になっています。テンプレートは登録した端末のSecure Enclave/TEE内に留まり、バックアップにもクラウドにも同期されません。
この設計により、仮にアプリやOSが侵害されても、攻撃者が得られるのは「認証成否のフラグ」までで、生体データそのものは漏れません。生体情報はパスワードと違って変更できないため、「そもそも外に出さない」ことが唯一の実効的な防御になります。
Keychain/Keystoreと鍵の非エクスポート性
生体認証と対になるのが、iOSのKeychainとAndroidのKeystoreが管理する鍵です。ここで決定的に重要なのが非エクスポート性(non-exportability)という性質です。
ハードウェアに支えられた鍵は、Secure Enclave/TEEの内部で生成され、その境界の外へは平文の鍵素材として一切取り出せません。アプリが手にできるのは鍵そのものではなく、鍵を指し示すハンドル(参照)だけです。データを暗号化・復号したり署名したりしたいとき、アプリは平文と操作内容をエンクレーブに渡し、エンクレーブが内部で鍵を使って処理し、結果だけを返します。
[アプリ側(リッチ側)] [Secure Enclave / TEE]
key = ハンドル(参照) -- 操作依頼 --> 実体の鍵で暗号化/署名
暗号文/署名 <-------------- 結果のみ ----- (鍵は外に出さない)
非エクスポート性の本質は、鍵の利用モデルを「鍵をメモリにコピーして使う」から「鍵のある場所へ処理を依頼する」へ反転させた点にあります。鍵がリッチ側のメモリに一度も現れないため、メモリダンプ・スワップ・コアダンプのいずれからも鍵を回収できません。iOSではkSecAttrTokenIDSecureEnclaveを指定して鍵を生成するとエンクレーブ内に鍵が閉じ込められ、AndroidではKeystoreの生成時にsetIsStrongBoxBacked(true)で専用セキュアチップ(StrongBox)に鍵を置けます。
非エクスポート性が成り立つと、鍵は端末という物理デバイスに束縛されます。端末を丸ごと複製しない限り鍵を持ち出せないため、認証情報の盗用が「デバイスの物理的奪取」まで難易度が跳ね上がります。この鍵の帰属は、コード署名で作者の同一性を担保する/mobile-development/mobile-app-signing-provisioning/ の秘密鍵管理とも問題意識が共通しています。
生体認証と鍵アクセスの「結合」
ここまでの2つ——生体照合と非エクスポートな鍵——を結び付けるのが、鍵に付与するアクセス制御条件です。鍵を生成する際、「この鍵を使うには生体認証の成功を要する」という制約を鍵の属性として刻み込めます。
- iOSでは
SecAccessControlに.biometryCurrentSetや.userPresenceを指定し、Keychainアイテムや鍵の利用条件として生体認証を要求します。 - AndroidではKeystore鍵生成時に
setUserAuthenticationRequired(true)を設定し、鍵を「認証で解錠されている間だけ使える」状態にします。
結合の実体はこうです。アプリが鍵で暗号操作をしようとすると、エンクレーブは「この鍵は生体認証を要求する」という属性を確認し、直前の生体照合が成功して解錠フラグが立っているかを検証します。フラグが立っていなければ操作は拒否されます。つまり生体認証の成功が鍵の解錠フラグを立て、その結果としてエンクレーブが暗号操作を許可する、という因果です。認証と鍵利用の判断がどちらも同じセキュア境界の内側で行われる点が肝で、リッチ側のアプリが「認証OKだったことにする」偽装を差し込む余地がありません。
iOSの.biometryCurrentSetやAndroidのsetInvalidatedByBiometricEnrollment(true)を使うと、指紋や顔を追加・削除して登録済み生体セットが変わった瞬間に鍵が自動的に無効化されます。これは、攻撃者が端末に自分の指紋をこっそり追加して鍵を使う攻撃を封じるための重要な設定です。逆にこの設定を外すと、生体を追加しても既存の鍵が使い続けられてしまうため、保護レベルとユーザーの利便性(生体を登録し直すたびに再設定が必要になる)のトレードオフを意識して選ぶ必要があります。
フォールバックと解錠状態の寿命
生体認証は万能ではありません。センサーが濡れている、顔が認識されない、連続失敗でロックアウトされる、といった状況が必ず起きます。そこでフォールバックとして、パスコード/PIN/パターンという知識要素へ退避する経路が用意されます。設計上重要なのは、フォールバック先が生体照合と同格以上の強度を持つ独立した認証経路であり、これも同じエンクレーブが検証する点です。生体を回避してパスコードへ抜けても、鍵の解錠は依然としてセキュア境界内で判断されるため、防御が弱いバイパス路にはなりません。
横にスクロール
もう一つ実務で誤解が多いのが、解錠フラグ(認証済み状態)の寿命です。
| 方式 | 解錠が有効な範囲 | 典型的な用途 |
|---|---|---|
| 操作ごとに認証 | 1回の暗号操作だけ有効。使うたびに生体を要求 | 送金・重要データ復号など高リスク操作 |
| 時間制限つき解錠 | 認証後の一定秒数だけ鍵を利用可能 | 短時間に複数回アクセスするUI |
| フォールバック許可 | 生体失敗時にパスコードへ退避 | 生体が使えない環境での継続利用 |
AndroidのsetUserAuthenticationParametersでは解錠の有効時間(timeout)と、生体・デバイス認証情報のどちらを許可するか(authentication type)を分けて指定できます。有効時間をゼロにすれば操作ごとに必ず認証を挟む厳格モードになり、正の値にすればその秒数内は再認証なしで鍵を使えます。この寿命の長さは、利便性と、端末を短時間奪われたときの被害範囲との綱引きで決めます。なお、これらの解錠状態はアプリのライフサイクルとも関わり、バックグラウンド遷移で無効化する設計が一般的です(アプリの状態遷移そのものは /mobile-development/ios-app-lifecycle-states/ を参照)。
生体認証は本人確認ではなく「鍵アクセスのゲート」であり、認証成功が解錠フラグを立てて暗号操作を許可する、という因果で説明できるようにします。鍵の非エクスポート性(鍵は境界外へ出ず操作だけを依頼する)と、生体テンプレートが端末外へ出ないこと、この2点がSecure Enclave/TEEの中核です。生体セット変更での鍵無効化、フォールバックの独立性、解錠フラグの寿命、が実装上の定番論点です。
まとめ
モバイルの生体認証は、Secure EnclaveやTEEというメインOSから分離された実行環境の中で生体照合を完結させ、生の指紋・顔データを一切外へ出さない設計です。その照合結果は、Keychain/Keystoreが管理する非エクスポートな鍵のアクセス制御条件として使われ、認証成功が解錠フラグを立てることで初めて暗号操作が許可されます。鍵はエンクレーブの外に出ないため、操作を依頼する形でしか使えず、端末という物理デバイスに束縛されます。生体セット変更での自動無効化、独立したフォールバック経路、解錠状態の寿命管理といった仕組みは、いずれも「認証と鍵利用の判断を同じセキュア境界の内側に閉じ込める」という一貫した原則から導かれます。この鍵保護は権限モデル(/mobile-development/mobile-app-sandbox-permissions/)と組み合わさって端末内の機密を守っており、より広い暗号・認証設計の一般論は /security/ と合わせて理解すると全体像がつかめます。
モバイル開発の記事ガイド
生体認証とセキュアエンクレーブを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
モバイル
比較で見る軸
難易度: advanced / カテゴリ: モバイル開発 / タグ数: 6
導入後に効く点
Keychain/Keystoreの鍵は「非エクスポート性」を持ち、鍵そのものは外に出せず、暗号化・署名の操作をエンクレーブに依頼する形でしか使えない。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- モバイル開発
- タグ数
- 6
判断チェックリスト
- 自社の用途が「モバイル / セキュリティ」に近いか確認する。
- 強みである「生体データはSecure Enclave/TEEという分離された実行環境の中だけで照合され、生の指紋・顔データはアプリにもOS本体にも渡らない。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。