
YubiKeyの仕組みを調べた
ふと思ったのですが、YubiKeyってどういう原理で認証しているのでしょうか。 2023年にYubiKey 5C NFCを使い始めて、その後USB-Aのモデルも買い足しました。当時は1Passwordのアカウントを守るために導入し、持ち歩く用と家に置いておく予備を登録する、という使い方を考えていました。 USBに挿して、必要なときに触れる。スマートフォンならNFCでかざす。使う側の操作はかなり少ないのですが、その裏で何をやっているのかは、ちゃんと整理したことがありませんでした。 あの金色の部分は指紋を見ているのか。そもそも電池がないのに、どうやって動いているのか。 今回はYubicoの公式資料と認証の仕様を読みながら、この辺りを調べてみました。手元のキーを分解したり、暗号処理を実測したりした記事ではありません。普段のログイン操作を、仕組みの側から見直した記録です。 YubiKeyの対応方式 YubiKeyはハードウェアの名前で、内部では複数の認証方式を扱えます。そのときに何を使ったかで、やり取りする情報も安全性の性質も変わります。 私が買ったYubiKey 5シリーズには、FIDO2、FIDO U2F、OATH、Yubico OTP、PIV、OpenPGPといった機能があります。全部が同じ暗号処理をしているわけではありません。公式の機能一覧を見ると、小さな本体の中に用途の違うアプリケーションが入っている、と考える方が近そうです。 代表的な違いを並べると、こうなります。 方式 何を使って確かめるか 利用時に外へ出るもの FIDO2 / U2F 公開鍵と対になる秘密鍵で署名する 認証要求に対する署名など OATH-TOTP / HOTP サーバーと共有した秘密からコードを計算する 6桁や8桁などの認証コード Yubico OTP 共通鍵で暗号化した、カウンターなどを含むデータ 長い文字列のワンタイムパスワード PIV / OpenPGP 保管した秘密鍵を使って暗号処理をする 署名や復号結果など、操作に応じた応答 ブラウザに「触れてください」と言われるときと、認証アプリで6桁の数字を表示するときは別の仕組みです。ここを混ぜると、サーバーに秘密を渡すのか、という問いの答えまで変わります。 なお、Security Keyシリーズのように、FIDOに機能を絞った製品もあります。 まずは、Webのセキュリティキーやパスキーで使うFIDO2を中心に見ていきます。 公開鍵と秘密鍵 FIDO系の認証で中心になるのは、公開鍵暗号を使った電子署名です。 公開鍵と秘密鍵は、対になる二つの鍵です。秘密鍵を使ってデータに署名し、サービスに渡した公開鍵を使ってその署名が正しいかを検証します。 電子署名は、相手だけに内容を読ませる暗号化とは目的が違います。確認したいのは、対象のデータについて、対応する秘密鍵を使える側が署名したかどうかです。 YubiKeyをFIDOの認証器として使う場合、サービスには公開鍵を登録し、秘密鍵を使う処理はキーの内部で行います。サービスはログインのたびに署名を確認することで、登録した鍵を今使える相手なのかを確かめます。YubicoのFIDO2開発者向けガイドでは、この構成と各参加者の役割が説明されています。 パスワードとの大きな違いは、ログインの際に、繰り返し使える秘密そのものをサービスへ渡さなくてよいことです。 公開鍵だけを見て同じように署名することはできません。公開鍵から秘密鍵を求めるのが現実的な計算量では困難になるよう、暗号方式と鍵の大きさが選ばれています。 メールアドレスなどの漏えいや、サーバー自体の侵害は別の問題です。ここで減らせるのは、保存していた公開鍵の漏えいが、そのまま本人として署名できる能力の漏えいになる危険です。 キーの登録 ログインの前には、キーの登録が必要です。サービスの設定画面で「セキュリティキーを追加する」といった操作をする部分ですね。 登録では、サービスからブラウザへ、ランダムな値やサービスの識別情報、アカウントに関する情報、利用できる暗号方式などが渡されます。ブラウザやOSはそれを認証器へ伝え、キーが認証用の鍵を生成します。返ってきた公開鍵やcredential IDなどを、サービスがそのアカウントに関連付けて保存します。登録の流れは、Yubicoの資料にも載っています。 credential IDは、使う認証資格情報を識別する値です。同じ物理キーで複数のサイトに登録できるのは、このような資格情報をサイトごと、登録ごとに扱っているからです。 このときサービス側がしているのは、「このアカウントには、この公開鍵で検証できる認証を許可する」という登録です。YubiKeyの製品名だけを見て本人扱いするわけではありません。 同じモデルのキーをもう一つ買ってきても、登録していなければログインできません。 登録する時点で、アカウントの持ち主も確認する必要があります。認証器は、誰のアカウントなのかを最初から知っているわけではありません。サービスが本人を確認してから、今後使う鍵を結び付けます。だから、鍵の追加を許す操作も大事ですね。 ログイン時のやり取り 登録が終わると、ログイン時にはサービスが新しいランダムな値を用意します。これはchallenge、チャレンジと呼ばれます。 「この値を含むデータに、登録した鍵で署名してください」という問いを出して、正しい署名が返ってくるか確かめるわけです。毎回違う問いを出すので、以前の正解をそのまま持ってきても、今回の問いへの答えにはなりません。Yubicoの認証処理の説明には、チャレンジと返される情報が整理されています。 たとえば、昨日のログインでは「A」という値に対する署名が正しかったとします。今日サービスが出した値が「B」なら、昨日の「A」への署名を送っても、今日の認証には使えません。署名を検証するときには、鍵が合うことに加えて、今回発行したチャレンジが応答に含まれていることも確認します。 新しい問いに答えられることが、その時点で鍵を使える証拠になります。コピーした過去の通信だけでは、新しい応答を作れません。 サービスは十分に予測しにくいチャレンジを生成し、どの認証操作に発行したのかを管理し、使用済みや期限切れの応答を受け付けないようにします。 操作の流れをかなり省略すると、こうなります。 sequenceDiagram participant S as サービス participant B as ブラウザ・OS participant Y as YubiKey participant U as 利用者 S->>B: 今回のチャレンジと認証条件 B->>B: 呼び出し元と認証先を検査 B->>Y: 認証先のIDと署名対象の情報 Y-->>U: 操作を要求 U->>Y: タッチ・必要ならPIN等で確認 Y->>Y: 秘密鍵で署名 Y-->>B: 署名と認証器の情報 B-->>S: 認証応答 S->>S: 公開鍵で署名と各条件を検証 署名するデータ ここまで「チャレンジに署名する」と書きましたが、実際のWebAuthnの認証応答はもう少し情報を持っています。








