
シェア:
ポールは、Vonageのプリンシパル・アーキテクトです。経験豊富なソフトウェアエンジニア、トレーナー、講演者として、Appleプラットフォームにおけるデータ駆動型ソリューションを専門とし、プロトタイピング、ベストプラクティス、そしてアジリティとのバランスに重点を置いています。
どんなパスキーにも、影のパートナーが必要だ
所要時間:1 分
パスキーは認証のセキュリティを劇的に向上させますが、すべての本人確認に関する問題を解決するわけではありません。このチュートリアルでは、新規登録やアカウントの復旧が依然として難しい理由、サイレント認証がそれらの課題をどのように解決するか、そしてVonage Verify APIを使用して完全なフローを構築する方法について学びます。
パスワードは、ソフトウェアにおける最も古くから存在する未修正のバグですであり、パスキーは、その秘密鍵を別の場所に移すのではなく、根本的に排除する初の解決策です。ワンタイムコードからマジックリンクに至るまでのこれまでのあらゆる対策は、単にその秘密鍵をより防御の堅い場所へ移動させたに過ぎませんでした。その歴史の全貌や、パスキーを機能させるための2つの手順について詳しく知りたい方は、私が 『第一原理から見るパスキー』に詳しくまとめています。
パスキーの採用が急速に広がっています。パスワードを公開鍵・秘密鍵のペアに置き換えることで、秘密鍵はユーザーのデバイスから決して外に出ず、サーバーには公開鍵のみが保持されるため、その仕組み上、フィッシング攻撃が機能しなくなるのです。 ブラウザは不正なサイトでの鍵の使用を単純に拒否し、盗まれた認証情報データベースは価値のない公開鍵のリストに過ぎなくなります。
しかし、こうした多くの利点があるにもかかわらず、パスキーには依然として2つの「扉」が守られていない。 誰かが初めて登録した際、その「扉」の前に誰が立っているのかを判別できない上、正当なユーザーが最後のパスキーを失った際に、再びアクセスさせることもできない。 この2つの「扉」こそが、まさに Vonageのサイレント認証 が真価を発揮する点です。この方式は、SIMカードと通信キャリアのネットワークを利用して電話番号の所有権を検証するため、コードを入力する必要がなく、フィッシャーが傍受できる情報も一切ありません。
フィッシング対策が施された2つの「ゼロ・フリクション」要素は、互いの死角を補い合っています。なぜこれらがこれほど相性が良いのかを見てから、 Verify v2 APIを使用してフローを構築してみましょう。
パスキーとWebAuthnの仕組み
パスキー は WebAuthn の認証情報です。登録時に、デバイスはセキュアなハードウェア内で鍵ペアを生成し、公開鍵をサーバーに送信します。サインイン時には、サーバーがランダムなチャレンジを発行し、デバイスは生体認証または PIN 入力後にそれに署名し、サーバーがその署名を Verify します。 親指を1回押すだけで、すでに多要素認証が実現されています。つまり、「所有物」(デバイス)と「本人性」(ジェスチャー)の2要素です。重要な点として、ブラウザはパスキーが作成されたドメイン上でのみパスキーを提供するため、ログインページが本物かどうかを判断するのは、もはや人間ではなくなります。
サイレント認証 Verify、SIMカードを用いてユーザーを認証します。 バックエンドは Verify v2 API を使用して検証を開始し、`check_url` を受け取ります。その後、ユーザーの端末がその URL をモバイル通信経由で開きます。通信事業者は、その電話番号を所有する端末からリクエストが送信されていることを自社の記録と照合して直接確認し、検証済みの GSM レスポンスを返します。 SMSは送信されず、6桁のコードを入力する必要もなく、攻撃者がユーザーからソーシャルエンジニアリングによって情報を引き出すための手がかりとなるような画面上の情報も一切表示されません。認証は通信事業者とモバイル端末の間で直接行われるため、従来のOTPフィッシングの手口は完全に排除されます。
この対称性に注目してください:
パスキーは、そのサービスに紐付けられた秘密鍵を所有していることを証明するものです。
サイレント認証は、電話番号に紐付けられたSIMカードの所持を証明するものです。
どちらの要素もユーザーに秘密情報を表示することはないため、ユーザーが漏らしてしまうような秘密情報を与えることもありません。
パスキーに仲間が必要なとき
「登録」「ログイン」「アカウント復旧」は、同じ質問を3回繰り返しているわけではないことに気づくと理解しやすくなります。 「新規登録」は「この人は誰か?」と問い、「ログイン」は「登録した本人か?」と問い、「アカウント復旧」は「本人であることを証明するものがなくなった今、本当に本人なのか?」と問うものです。
パスキーは、サインインに関する質問に対しては素晴らしい答えを提供しますが、他の2つの質問に対してはまったく答えを提供しません。
「初日問題」
パスキーは、ログインしている人物が登録者本人であることを証明することしかできません。誰が登録したかについては何も示しません。サービス開始当初はパスキーがまだ存在しないため、パスワードレス登録の多くは、システム全体の中で最も脆弱な部分、つまり、未確認のメールアドレス、あるいはフィッシング攻撃の標的となり得るリンクによって確認されたメールアドレスに依存することになります。 もし攻撃者が被害者よりも先に victim@example.com というアカウントを作成し、そこに自身のパスキーを紐付けてしまった場合、ユーザーが実際にログインする前から、アカウント乗っ取りに先立つ問題が発生することになります。
登録を、バックグラウンドで検証済みの電話番号に紐付けることで、状況は一変します。Accountは、通信事業者がその端末で有効であることを確認した電話番号に基づいて作成され、その後に初めてパスキーの手続きが実行されます。これにより、パスキーは、ユーザーが単に打ち込んだ情報ではなく、実際に検証された身元情報を暗号的に拡張するものとなります。
The Day One Problem
「ラストデバイス問題」
2つ目の扉は「復旧」です。 1つのデバイスに紐付けられたパスキーしか持っていないユーザーは、スマホを1台失うだけでAccountにアクセスできなくなってしまいます。また、同期されたパスキーであっても、ユーザーがプラットフォームのAccountに引き続きアクセスできることが前提となっています。多くの製品では、メールで送信される「マジックリンク」に頼っていますが、これにより、パスキーによって排除された要素――受信トレイに保存され、パスワードで保護されたベアラー・トークン――が、知らぬ間に再び導入されてしまうのです。
Silent Authは、復元対象と同じセキュリティレベルを備えた復元経路を提供します。新しい端末でも同じ電話番号が利用可能になります。通信事業者がSIMカードを保証し、ユーザーがパスキーを再登録する過程において、攻撃者が傍受可能な通信経路をコードが通過することは一切ありません。
合流流量
以下に、エンドツーエンドのライフサイクルを示します:
登録: Silent Authで電話番号をVerifyし、Accountを作成してから、最初のパスキーを登録してください。
それ以降のすべてのサインインでは: パスキーのみ。ワンタッチで、通信事業者とのネットワーク通信を一切行わず、デスクトップを含むあらゆるデバイスで動作します。
復元、または同期を行わない新しいデバイス: 再度「サイレント認証」を行い、その後、代替パスキーを登録してください。
オプションの手順: リスクの高い操作(支払い、メールアドレスの変更、未認証のブラウザからの新しいパスキーの追加など)を行う際は、2つ目の独立した認証要素として、バックグラウンドでサイレント認証チェックを実行します。
The Combined FlowPasskeysは日常的な処理を担当し、Silent Authは特殊なケースに対応します。 それでは、面白い部分を書こう。
前提条件
お客様のアプリケーションは、本番環境での利用に向けてネットワークレジストリに登録されました(開発中はサンドボックスが利用可能です。詳細については後述します)。
セッションを維持できるバックエンド。以下のスニペットでは、純粋なcurlとブラウザのJavaScriptを使用しているため、お好みのスタックに合わせて変換することができます。
「サイレント認証」の段階をテストするための、SIMカードとモバイルデータ通信機能を備えた携帯電話。
ステップ 1:登録時に番号を自動的にVerifyする
新規ユーザーが電話番号を送信すると、バックエンドで認証処理が開始されます。その「魔法」が働くのが `workflow` 配列です。まず `silent_auth` が実行され、Silent Auth が実行できない場合には SMS による代替認証が行われます。
curl -X POST https://api.nexmo.com/v2/verify \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"brand": "ACME Inc",
"workflow": [
{ "channel": "silent_auth", "to": "447700900000" },
{ "channel": "sms", "to": "447700900000" }
]
}' この応答には request_id が含まれており、サイレント認証の場合は、 check_urlが含まれています:
{
"request_id": "b3a2f4d0-1234-4d6e-9f00-example00000",
"check_url": "https://api-eu-3.vonage.com/v2/verify/b3a2f4d0.../silent-auth/redirect"
} クライアントに check_url クライアントに渡し、デバイスにモバイル通信経由でそれを開かせるようにします。これが「サイレント認証」の唯一の必須要件です。リクエストがWi-Fi経由で送信されると、通信事業者はそれを検知できず、検証に失敗します。 VonageのiOSおよびAndroidクライアントライブラリは、Wi-Fiに接続されている場合でもリクエストをモバイルデータ通信経由で送信するように強制するために存在するものです。そのため、独自に実装するのではなく、これらを活用してください。
このデバイスは、通信事業者のネットワークへの短いリダイレクト経路を経由し、コードを取得して戻ってきます。このコードをクライアントがバックエンドに送信すると、バックエンドはそれをVerifyに転送します:
curl -X POST https://api.nexmo.com/v2/verify/$REQUEST_ID \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{ "code": "'$CODE'" }' A "status": "completed" は、通信事業者がそのSIMカードを保証していることを意味します。ユーザーはVerify済みであり、ここで注目すべき点はここです:、 ユーザーは、その一連の過程を一切目撃していません。認証コードも届かず、何も入力しませんでした。ユーザーの視点から見れば、 電話番号を入力しただけで、自動的にログインされたのです。
開発中は、 "sandbox": true ワークフローエントリに追加し、Network Registry Playground を使用することで、通信事業者のサービスエリア外でもフロー全体をテストできます。
ステップ2:最初のパスキーを登録する
今、まさに今だけ、Accountを作成し、すぐにWebAuthnの登録手続きを行ってください。最近のブラウザのネイティブAPIには、依存関係は一切必要ありません:
// The server generated these options, including a random challenge
// and the user object: { id: userHandle, name: phoneOrEmail }
const options = PublicKeyCredential.parseCreationOptionsFromJSON(optionsJSON);
// The OS prompts for Touch ID / Face ID / Windows Hello and mints
// the key pair inside secure hardware. The private key never leaves.
const credential = await navigator.credentials.create({ publicKey: options });
await fetch("/registration", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ credential: credential.toJSON() }),
}); 2つのサーバー側の設定により、通常の認証情報をパスキーに変換できます。これらの設定では、 検出可能な 認証情報(resident_key: "required")を要求することで、ユーザー名なしでサインインできるようにし、 ユーザーによる認証 を要求します。 user_verification オプションは、ブラウザに対してジェスチャーの要求を行うだけであることに注意してください。認証ツールの応答に含まれる「ユーザー認証済み」フラグを確認するのは、サーバー側での検証ステップにおいて行わなければなりません。そうしないと、改ざんされたクライアントが生体認証をスキップし、多要素認証ログインを気付かれずに単一要素認証へと格下げしてしまう可能性があります。 その確認が完了したら、発行したチャレンジに対してレスポンスを検証し、公開鍵を保存します。これで、そのアカウントには、通信事業者によって検証された電話番号に紐付けられた、フィッシング攻撃に強い認証情報が設定されます。
設計上の注意点として、Verifyされた電話番号はパスキーの「人間が読みやすいラベル」(作成オプションの「user.name」)として最適です。WebAuthn仕様書でも、まさにこの目的のために、メールアドレスやユーザー名と並んで電話番号が明示的に挙げられています。 ただし、これをユーザーハンドル(user.id)として使用することは絶対に避けてください。ユーザーハンドルは、不透明なランダムな識別子のままである必要があります。
ステップ3:パスキーだけでサインインする
日常的なサインインでは、Verify API は一切使用されません。ユーザーがボタンをタップすると、ブラウザにアカウントピッカーが表示され、1回の操作でサーバーからのチャレンジに署名すれば、完了です。
ここで、「セキュリティをさらに強化するために、すべてのサインイン時に『Silent Auth』を実行すればいいのではないか」と疑問に思うかもしれません。その答えは、そうすると、必要のない機能に費用を払うことになり、必要な機能が失われてしまうからです。 パスキーによるサインインでは、登録済みデバイスの所有と最新の生体認証情報がすでに確認されており、デスクトップでも、Wi-Fi環境でも、電波が届かない地下室でも機能します。これらはまさに、キャリアチェックではカバーできない場所です。 また、Silent Authのチェック1回ごとに、ネットワークの往復通信(およびチェックごとの手数料)が発生します。これはライフサイクルの特定の局面では問題ありませんが、ログインのたびに発生するコストとしては顕著です。つまり、キャリアチェックは、パスキーが機能しない場合にのみ使用すべきなのです。
ステップ4:回復:初日と同じ扉
ユーザーが最後のパスキーを紛失した場合の復旧手順は、ステップ1を実行した後、再びステップ2を実行するだけです。具体的には、登録済みの電話番号を自動的に確認し、短期間有効な復旧セッションを開き、新しいパスキーを生成してもらいます。 SMSによるフォールバック機能を含むこのワークフローでは、新しい携帯電話にSIMカードは挿入されているものの、現在デスクトップ端末を使用しているユーザーといった厄介なケースもすでに処理されており、その場合はVerifyがワークフローを次のチャネルへと移行させるだけです。
ユーザー認証に関しては、万能な解決策など存在しないため、このトレードオフについて率直に認識しておく価値があります。電話番号を基にしたアカウント復旧は、その電話番号自体のライフサイクルに左右されます。 電話番号は再割り当てされることもあり、SIMスワップ詐欺も存在します。そのため、通信事業者の記録を参照して直近のSIM変更を確認する「Silent Auth」は、SMSコードよりもはるかに高いセキュリティ基準を満たしており、復旧処理を厳重な審査を行う好機と捉えるべきです(利用可能な他のすべてのチャネルに通知し、高額な取引を遅延させ、そのイベントをログに記録する)。
なぜこの組み合わせがうまくいくのか
一歩引いて見ると、その光景は心地よいほどすっきりとしている:
| パスキー | サイレント認証 |
~の所持を証明する | セキュアハードウェア内の秘密鍵 | 通信事業者の記録にあるSIMカード |
推薦者: | ブラウザとデバイスのセキュアハードウェア | 携帯電話事業者 |
ユーザー体験上の障壁 | ひとつの仕草 | まったくない |
フィッシングの標的となり得る秘密情報がユーザーに表示される | なし | なし |
動作中 | どんなデバイスでも、どんな接続環境でも | モバイルデータ通信中のスマートフォン |
実行されると | すべてのサインイン | 登録、復旧、ステップアップ |
攻撃者には以下が必要となる | 物理的なデバイスに加え、生体認証またはPIN | 被害者の有効なSIMカード |
あらゆる認証システムには「ブートストラップ問題」と「回復問題」が存在し、ほとんどのソリューションは、まさにその2つの局面において、知らぬ間にフィッシング攻撃を受けやすい通信経路へとダウングレードしてしまいます。パスキーとサイレント認証を組み合わせることで、ライフサイクル全体を通じて、ユーザーに秘密情報を一切開示することなく認証を行うことが可能になります。 ブラウザはオリジンを確認し、通信事業者はSIMを確認するため、ユーザーはフィッシャーが偽造しうる要素を判断するよう求められることは決してありません。つまり、ユーザーが説得されて漏らしてしまうような秘密情報は存在しないのです。
そして、それがタイトルが約束していた仕組みです。「サイレント・オーソ」は、いわゆる「サイレント・パートナー」です。このパートナーは、ベンチャーを立ち上げるための信託基金を拠出し、事業が危機に陥った際には再び介入しますが、それ以外の時間は表舞台に現れることなく、日々の運営は「パスキー」が担当します。
結論
どちらかの半分についてさらに詳しく知りたい場合は、 『サイレント認証ガイド』 ではカバレッジとネットワークレジストリへの登録について解説しており、 「サイレント認証の入門」チュートリアル では、 check_url Node.jsバックエンドを用いたエンドツーエンドのフローを解説しています。また、 ベストプラクティスの記事 では、フォールバックワークフローの設計について詳しく解説しています。
ネットワークベースの認証とパスキーを組み合わせていますか、あるいはその予定はありますか?その導入状況について、ぜひお聞かせください。
ご質問がある場合、またはあなたが作っているものを共有したい場合は、こちらをクリックしてください。
登録する 開発者ニュースレター
フォローする X(旧ツイッター)最新情報
チュートリアルを見る YouTubeチャンネル
LinkedInの LinkedIn の Vonage デベロッパーページ
開発者体験の向上にご協力ください。以下の 「開発者の声」フィードバックにご協力ください
最新の開発者向けニュース、ヒント、イベント情報をお届けします。