- セキュリティトークンは秘密鍵をデバイスの外へ出さずにデバイス内で署名し、ユーザーの物理的な操作を要求することで、リモート攻撃者が任意の署名を作ることを難しくする
- SSH認証、U2F、パスワードレスのローカルログイン、
sudo、gitのコミット署名に使え、最新のノートPCやスマートフォンの内蔵セキュリティ機構がYubiKeyの代わりになりうる
ssh-keygen -t ed25519-skで作られる「秘密鍵」ファイルは実際の秘密鍵ではなく、トークン内の鍵を指すハンドルであり、同じトークンを使えば別のコンピューターでも同じSSH鍵ファイルを生成できる
- MacBookではsecure elementをSSH鍵として設定し、Touch IDベースのSSHログインが可能で、gitのコミット署名ではファイルパスではなく
ssh-agentとkey::形式のuser.signingKey設定が必要だった
- セキュリティトークンは紛失すると秘密鍵を復旧できず、繰り返しタッチに慣れてしまうユーザビリティ上のリスクがあり、WindowsノートPCではWindows Helloが顔認証・指紋・PINでSSH鍵の使用を確認できた
セキュリティトークンの利点と限界
-
リモート攻撃を防ぐ中核構造
- セキュリティトークンは秘密鍵/公開鍵のペアをデバイス内に保持し、公開鍵は簡単に取り出せる一方で、秘密鍵はデバイスの外に出ないようにする装置である
- 署名するデータパケットをデバイスに送ると、秘密鍵でデバイス内署名が行われ、通常は点滅するタッチボタンを押すといった物理的なユーザー操作が必要になる
- リモート攻撃者がコンピューターにアクセスできても、ユーザーが現実世界で操作しない限りセキュリティトークンは任意の署名を行わないため、
~/.sshディレクトリにSSH秘密鍵/公開鍵の完全なペアをファイルとして置く方式より良さそうに見える
- SoloKeysやNitrokeysのように、FOSSファームウェアを望む選択肢もある
- より高機能なセキュリティトークンは内蔵指紋リーダーのような生体認証機能を追加するが、中核は秘密鍵をデバイス外へ出さない構造にある
-
使い勝手から生じるリスク
- ユーザーがセキュリティトークンの点滅のたびに押すことに慣れると、悪意ある要求にも無意識に応答してしまう可能性がある
- 連続した署名作業の最中にトークンを何度も押す状況では、もう一度点滅する要求に本当に気づくのは難しいことがある
- AppleとMicrosoftはスマートフォンの認証アプリで、アクセス要求ごとにランダムな数字コードを表示してユーザーに入力させる方式を使っているが、これは煩雑で、TOTPを使うAuthyやGoogle Authenticatorアプリと比べたときのセキュリティトークンの使い勝手上の利点を弱める
-
紛失とバックアップの問題
- セキュリティトークンを失くすと、その秘密鍵は永久に失われ、バックアップする方法もない
- 複数のアカウントから締め出されるリスクを避けるには、セキュリティトークンを買う際に少なくとも2個購入し、同じサービスに登録しておく必要がある
- 代替として、BIP 39のように秘密鍵を人間が読める単語リストに変換して書き留めるバックアップ・復元方式がある
- 秘密鍵がセキュアエンクレーブの外に出せる場合、ユーザーをだまして単語リストを不適切な場所に書かせるフィッシング攻撃も可能になる
- すべてのセキュリティトークンを失う可能性が本当に心配なら、BIP 39の単語リストはシステムへのアクセスを取り戻すための最後の手段になりうる
SSHとgitでセキュリティトークンを使う
-
SSH秘密鍵をセキュリティトークンに置く
- 通常、
ssh-keygenを実行すると秘密鍵全体を含むファイルのペアが作られる
- セキュリティトークンに秘密鍵を置くには、YubicoのFIDO/U2Fガイドに従ってlibfido2をインストールし、セキュリティトークンを挿した状態で
ssh-keygen -t ed25519-skを実行する
- この場合もファイルのペアは生成されるが、「秘密鍵」ファイルは実際の秘密鍵ではなく、セキュリティトークン内にある秘密鍵を指すハンドルである
- 同じセキュリティトークンで
ssh-keygen -t ed25519-skを再実行すれば、どのコンピューターでも同じ秘密鍵/公開鍵ファイルを作成できるため、SSHアクセス権は特定のコンピューター上の特定のファイルではなく、セキュリティトークンとともに移動する
-
git認証とコミット署名
- セキュリティトークンを押す状況の約90%はgitの利用が理由である
- git forgeはpushやpullのためのSSH認証を実装しており、上で生成した
id_ed25519_sk.pubファイルをアップロードすれば、セキュリティトークンの鍵ペアを利用できる
- gitはコミット署名にもSSH鍵をサポートしており、GitHubドキュメントのSSH鍵で署名鍵を設定するに従ったうえで
git config --global commit.gpgsign trueを実行すると、すべてのコミットが自動署名される
- git forgeがコミットを本人の署名として認識するには公開鍵を再度アップロードする必要があり、このフィールドは通常SSH認証用のフィールドとは別である
-
コミット署名の不便さ
- 長いコミット列をrebaseするときは、すべてのコミットに再署名しなければならない
- 指紋リーダー付きのYubiKeyは、数十件のコミットを連続署名するには指紋認識の失敗率が高すぎて、使うのをやめることになった
- gitの「rebase/amend中心」ラッパーであるjujutsuには、push時点でのみコミットに署名する方法がある
-
Linuxのローカルログインとsudo
MacBookのsecure elementをSSH鍵として使う
- USB-Cポートにセキュリティトークンを挿しっぱなしにすると、誤って落としたりぶつけたりしたときにポートやトークンを壊しかねない小さなてこのように突き出した状態になる
- 2020年モデルのM1 MacBook Airで、Arian van Puttenのガイドに従って内蔵セキュリティ要素をSSH鍵として設定した
sc_auth create-ctk-identity -l ssh -k p-256-ne -t bio
ssh-keygen -w /usr/lib/ssh-keychain.dylib -K -N ""
- このコマンドは
id_ecdsa_sk_rk秘密鍵/公開鍵ファイルのペアを作成し、それらのファイルを~/.sshディレクトリへ移動した
- ここでも秘密鍵ファイルは実際の秘密鍵ではなく、デバイス内の鍵に対するハンドルなので、公開的に貼り付けられる形式である
- ホームラボのサーバーに公開鍵をauthorized keyとして追加するには、次のように実行する
ssh-copy-id -i ~/.ssh/id_ecdsa_sk_rk.pub <server nickname>
- その後、
~/.ssh/configに次の設定を追加した
Host *
IdentityFile ~/.ssh/id_ecdsa_sk_rk
SecurityKeyProvider=/usr/lib/ssh-keychain.dylib
ssh <server nickname>を実行すると、ログイン前にmacOSが自動で指紋認証の要求を表示し、その後SSHログインが正常に進む
MacBookのsecure elementでgitコミットに署名する
git config --global user.signingKey /Users/ahelwer/.ssh/id_ecdsa_sk_rkを設定し、.ssh/allowed_signersファイルを更新しても、gitのコミット署名はすぐには動かなかった
- gitはコミット署名に失敗し、
device not found?のようなエラーを出力した
error: Signing file /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO
Confirm user presence for key ECDSA-SK SHA256:oQDA2SNYb2MoSQcxJVSmWyAeAWPqMp7rxliBRfi87as
Couldn't sign message: device not found?
Signing /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO failed: device not found?
fatal: failed to write commit object
- 解決策は、
~/.sshディレクトリ内のファイルを直接参照する代わりにssh-agentを使うことだった
- 上記のチュートリアルに従って、次のコマンドで鍵ペアをssh-agentに登録した
ssh-add -K -S /usr/lib/ssh-keychain.dylib
- その後、
user.signingKeyにはファイルパスではなく、~/.ssh/id_ecdsa_sk_rk.pubの内容の先頭にkey::を付けた鍵そのものを~/.gitconfigに入れた
[user]
name = Andrew Helwer
signingKey = "key::sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGxFEdnIg6ppz+pQCdd1eisjOV4gxrjMv1Y4SbtdLoSm6CJCgPZ6q7lnNyuQQsdnS4/Tllsc656AQL7BO3OS47cAAAAEc3NoOg== ssh:"
- この設定の後、MacBookのsecure element内の鍵でファイルに署名し、GitLab Pagesサイトへpushできた
WindowsとLinuxで試した結果
- 会社支給のWindowsノートPCでも簡単な実験を行った
winget install Microsoft.OpenSSH.preview
ssh-keygen -t ecdsa-sk
- このコマンドでも秘密鍵/公開鍵ファイルのペアが生成され、SSH接続時にはWindows Helloの標準ログインフローを通じて、顔認証・指紋・PINのいずれかで確認できた
- Linuxでは、同様の実ユーザー存在確認の後ろにsecure elementがぶら下がっているノートPCへアクセスできず、デモはできなかった
1件のコメント
Lobste.rsの意見
素晴らしい記事で、こういうことが可能だという事実を示しているだけでも非常に有益
個人的には、これを動かすのに適したライブラリのバージョンは見つけられなかったが、1Password 8 が SSH キーを安全に保存し、エージェントが生体認証でキーのロック解除をサポートしていることを知った
なので今では、指を置くだけで git 作業もできるし、SSH ホストにもログインできる
ガイド: https://developer.1password.com/docs/ssh/get-started/
これはMac 専用のように見える
そうしたセキュア要素を持つ Linux システムは試せておらず、自分の Linux ワークステーションには V1 TPM があるが、署名処理が実際のユーザー存在確認の後にのみ実行されることを保証する方法は特に分からない
Framework のような Linux ノート PC を持っている人なら試せるかもしれない。Asahi でも実際に動く可能性はある
では、提供されたprivate key ファイルの中には正確には何が入っているのか?
ssh:の部分、つまりアプリケーションは passkey の origin に相当し、ホストやドメインごとのresident keyを作るときに役立つ例えば、同じ物理 YubiKey の中でも用途ごとにキーを分離するのに使っている
flagsはハードウェアがキーをどう扱うべきかを指定する [1]。エージェントが独自の制約を追加することもある技術的には、FIDO キーには別の blob や拡張も保存でき、前職では認証と一緒に X.509 公開鍵のような補助資格情報を渡すために使ったことがある。かなりクールな仕組みだ
[1]
外側のラッパーにはマジック値
openssh-key-v1\0、cipher=none、kdf=noneがあり、暗号文ではない公開鍵 blob の 74 バイトには、キータイプ
sk-ssh-ed25519@openssh.com、32 バイトの Ed25519 点fdcce889…03e7852b、アプリケーションssh:が入っており、この値が SSH と WebAuthn の FIDO 資格情報を名前空間で分離している秘密セクションは 248 バイトで、
cipher=noneなので平文。checkint1 == checkint2 == 0x46744267のランダム値、繰り返されたキータイプと公開鍵、アプリケーションssh:、flags: 0x01が入っているこのフラグは
USER_PRESENCE_REQUIREDを意味し、タッチは必要だが PIN / ユーザー検証はなく、非 resident key であるkey_handleは 128 バイトの不透明な資格情報 ID で、authenticatorGetAssertionに渡され、デバイスが内部的に解釈して Ed25519 シードを復元するそのほかに空の
reserved、コメントahelwer@ah-mbair.local、パディング01 02 03があるこの記事は SSH だけを扱っているようだが、自分のコンピュータのSecure Enclave や TPM を FIDO2 または U2F キーとして使う方法はあるのだろうか?
Passkey も、Web サイトごとに別個の秘密鍵を使うこうした方式の一形態だ
この事実を見ると、ハードウェアに紐付けられる非対称または HMAC API キーのサポートがもっと一般的でないのは不思議に感じる
WebAuthn、DBSC(Device-Bound Session Credentials)、OAuth2 DPOP のように、この方向をさらに後押しする仕様が増えているのはうれしい