Quantcast
Channel: IdM実験室
Viewing all 769 articles
Browse latest View live

そういえばHyperledger AriesもOID4VCに対応するようです

$
0
0

こんにちは、富士榮です。

先日のOpenID Summit Tokyo 2024のJosephがOpen Wallet Foundationの話をしてくれました。その中でHyperledger AriesもOpenID for Verifiable Credentials関係に対応する、というプロジェクトの説明がこっそりありましたが、どうも相互運用性に関するワークショップがある(あった)らしいです。

↓はJosephのスライド。一番下にしれっとAgent Framework Javascriptについて記載があります。


こんなことが書いてあるので、やっぱりAnonCred/DIDComm/Indy専用っていうイメージを払拭したいんでしょうね。

Previously known as Hyperledger Aries Framework Javascript, it initially heavily relied on Hyperledger standards such as DIDComm, Indy, and AnonCreds. However, with advancements in verifiable credential technology and the emergence of new standards, the framework underwent multiple refactoring and modularization processes to maintain interoperability.

まぁ、もちろんHyperledgerプロジェクトもOpen Wallet FoundationもLinux Foundationの傘下ということもありこの流れは順当なんだとは思いますが。


というところで、今朝(というか夜中)こんなワークショップがあったようです。

https://wiki.hyperledger.org/pages/viewpage.action?pageId=113311983


相変わらず日本人には優しくない時間帯なのでレコーディングに期待して熟睡しましたが、こちらをみてもこんなことが書いてあります。

The main goal of the workshop is to invalidate a misconception that Hyperledger Aries SSI projects can only work with CL AnonCreds, DIDComm and Indy Ledger. We are going to apply Credo (Agent Framework JavaScript) for issuance and verification of Verifiable Credentials in W3C format, use OIDC4VC for credentials exchange, and leverage external permissionless ledgers as a VDR. Moreover, we are going to show a synergy between two graduated Hyperledger projects: Aries and Besu. It will be done by means of a new Indy-Besu effort and repo.

このワークショップの主な目的は、Hyperledger Aries SSIプロジェクトはCL AnonCreds、DIDComm、Indy Ledgerでしか動作しないという誤解を無効にすることです。W3Cフォーマットの検証可能なクレデンシャルの発行と検証のためにCredo(エージェントフレームワークJavaScript)を適用し、クレデンシャル交換のためにOIDC4VCを使用し、VDRとして外部のパーミッションレス台帳を活用する予定です。さらに、卒業した2つのHyperledgerプロジェクト間の相乗効果を示す予定です: AriesとBesuである。それは、新しいIndy-Besuの取り組みとレポによって行われる。(Deeplによる微妙な機械翻訳)

やはり早くから取り組んでいただけあってHyperledgerな呪縛が付きまとうんでしょうね。早く誤解を解きたいっていう気持ちが溢れています。

こんな組み合わせのデモやハンズオンがあるので内容は面白そうですね。

Demo:
• Non-Indy VDR + CL AnonCreds: Hyperledger AFJ + Cardano as VDR
• Non-Indy VDR + W3C VC: Hyperledger AFJ + cheqd as VDR
• OIDC4VC in Hyperledger AFJ
Hands-on:
• Non-Indy VDR + CL AnonCreds: Hyperledger AFJ + Hyperledger Besu (Indy Ledger 2.0) as VDR
• Non-Indy VDR + W3C VC: Hyperledger AFJ + Hyperledger Besu (Indy Ledger 2.0) as VDR


W3C VCなのか〜ってのはありますが。

この辺は今後の動向に注目かもしれません。


OAuth 2.0 Security Best Practice, OpenID for Verifiable Credential Issuance ID-1など続々と

$
0
0

こんにちは、富士榮です。

先週は色々と仕様のアップデートが動いた週でした。


OAuth 2.0 Security Best Current Practice(OAuth2.0 BCP)

https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics


OAuth 2.0を実装する際のセキュリティに関するベストプラクティスに関するドキュメントです。(そのまま)


OpenID for Verifiable Credential Issuance

https://openid.net/review-of-proposed-implementers-draft-of-openid-for-verifiable-credential-issuance/


こちらは言わずと知れたVerifiable CredentialをOpenIDプロトコルに乗せて発行するための仕様ですね。Verifiable Presentationに関する仕様はすでにImplementers Draft 2まで出ていますが、発行側は初のImplementers Draftですね。ようやく実装者は本気で実装が開始できる段階まで来ている、ということです。


それぞれの細かい内容はおって紹介したいと思います。 

OAuth2.0 Security Best Current Practiceを読んでみる(1)

$
0
0
こんにちは、富士榮です。

昨日ポストした通り、OAuth2.0 Security Best Current PracticeがLast Callの段階に進みました。2024年2月22日までのレビュー期間を経て正式にRFCに向けたステップに進みます。

ということで少しだけこのドキュメントを見ていきたいと思います。

何に関するドキュメントなのか

Abstract、Introductionを軽く要約すると、
  • 対象はRFC6749(The OAuth 2.0 Authorization Framework)、RFC6750(The OAuth 2.0 Authorization Framework: Bearer Token Usage)、そしてRFC6819(OAuth 2.0 Threat Model and Security Considerations)の3点
  • OAuth2.0のフレームワークの活用が進むにつれ、当初想定していた以上に重要なシナリオ(金融やヘルスケア、行政サービスなど)でも利用されるようになってきた
  • 当然さまざまな攻撃パターンも編み出されてきており、基本的にはRFC6819により対応してきてはいるが継続的に対応していくことが重要となってきている
  • そのため、このドキュメントでは最新のセキュリティ上の推奨事項を提供する
ということになっています。

文章の構造

目次を眺めると、
  • ベストプラクティス
  • 攻撃モデルの更新
  • 攻撃パターンと緩和策
という形になっています。

まずはベストプラクティスに従いアーキテクチャを設計を行い、RFC6819発行時点に加えて最新化された攻撃モデルを念頭に置きつつ、各攻撃パターンに対する緩和策を実装していく、という流れでシステム化を進めていく、という使い方が想定されているのだと思います。

今回はベストプラクティスの部分を見ておきましょう。

ベストプラクティス

以下のベストプラクティスが定義されています。
  • リダイレクトベースのフローの保護
    • 全体
      • オープンリダイレクト対策として登録済みredirect_uriとの完全一致を検証すること
      • CSRF対策をすること。PKCEやstateですね
      • Mix up攻撃に対策すること
      • 資格情報を含む情報をリダイレクトする際は誤って転送しないようにすること
    • 認可コードグラントの場合
      • 認可コードインジェクション対策を行うこと。PKCEですね
    • インプリシットグラントの場合
      • トークン漏えい対策をしない限りは使わないこと
  • トークンリプレイ対策
    • アクセストークン
      • DPoP、mTLSなどの送信者制限トークンを利用すること
    • リフレッシュトークン
      • パブリッククライアントの場合は送信者制限を行うかローテーションを行うこと
  • アクセストークンの権限の制限
    • 必要最低限の権限に絞ること
    • 特定のリソースサーバで利用するようにAudienceを絞ること
  • リソースオーナーパスワードクレデンシャルグラント
    • 利用してはならない
  • クライアント認証
    • 可能な限りクライアント認証を実装すること
    • mTLSやprivate_key_jwtなどを用いることを推奨
  • その他
    • RFC8414のOAuth Authorization Server Metadataを使うことは有用であるため認可サーバはメタデータを公開することを推奨
    • リソースサーバがクライアントを混同するような可能性のある属性を許可してはいけない
    • クライアントとリソースサーバの間はTLSで保護されるべきである
    • 認可応答はlocalhostおよびリダイレクトを使うネイティブアプリ(カスタムURIスキーム)を除き暗号化されていないネットワークを許可してはならない
    • postMessageで認可応答を送信する場合は送信開始者と受信者の厳密な検証を行うこと
    • 認可エンドポイントはユーザーエージェントが直接アクセスするためCORSを許可してはいけない
結構たくさんありますが、原則的なことが多いので実装する際のチェックとして利用すると良いと思います。

次回は続きを紹介したいと思います。


OAuth2.0 Security Best Current Practiceを読んでみる(2)

$
0
0

こんにちは、富士榮です。

前回に続き、OAuth2.0 Security Best Current Practiceを読んでいきます。

今回は2つ目のブロックである「攻撃モデルの更新」です。今回は短めです。


攻撃モデルの更新

RFC6819に定義されている攻撃モデルの最新化しているセクションです。ここで定義された攻撃モデルに対して次のセクションで緩和策が定義される、という流れになっています。
定義されているのは、以下の5つのモデルです。
  • A1)攻撃者がブラウザやサーバに任意のエンドポイントをセットアップできる環境にあるケース
    • まぁ正直これができる環境だともう何も信じられなくなりますが、攻撃者はユーザを独自の認可サーバに誘導したり、認可コードやトークンの搾取ができてしまう可能性を考慮する必要があります。
  • A2)通信ネットワークを完全に制圧されているケース
    • これも当然ですが盗聴やなりすましが容易にできてしまう可能性を考慮する必要があります。
  • A3)認可レスポンスを攻撃者が見れてしまうケース
    • レスポンスの改ざんはできなくても見られてしまうケースは想定しておかないといけません。オープンリダイレクタなどが典型ですね。
  • A4)同様に認可リクエストを攻撃者が見れてしまうケース
    • リクエストが漏えいすることで攻撃者に情報を与えてしまう、というのはよくあることですね。
  • A5)アクセストークンを攻撃者が取得できてしまうケース
    • リソースサーバが攻撃者によって侵害されてしまっているケースなどが考えられますね。
これらの攻撃モデルのうち、A3〜A5は通常A1もしくはA2と同時に発生します。また、ここは大切なポイントだと思いますが「複数の攻撃者が協力して攻撃する可能性」についても考慮しておく必要がるとも述べられています。

次回は最終回、攻撃パターンと緩和策について読んでいきたいと思います。

Authentrendのカード型のPasskeyを試す

$
0
0

こんにちは、富士榮です。

なんだかステマっぽいですがそういう訳ではありません。

昨年末のFIDOアライアンスのイベントでAuthentrendの方から最新のカード型のパスキー「ATKey.Card NFC Bio-ID」をいただいたので試してみます。(いただいた段階では管理用のアプリがストア公開されていなかったので試せずにいたのですが2月に入りようやく公開されたので試せるようになりました)

こちらが製品のページです。

https://authentrend.com/atkey-card-nfc/

 こんなやつです。


バッテリーなしで動作するICカードタイプのセキュリティキーで、インターフェイスはNFCなのでパスキーとしての利用以外にも扉の開閉などにも使えます。(もちろん扉側の管理システムがNFCに対応していてカードを登録すれば、ですが)

パスキーとしてみた場合は単なるNFCタイプの指紋センサー付きの外部セキュリティキーとして使えます。

早速試してみましょう。


カードのセットアップ

カードをアクティブ化するにはiOS版かAndroid版の管理アプリをインストールする必要があります。(現状、Android版はベータ版です)
アプリケーションは先述の製品ページのQRコードからマーケットを開くとダウンロードできます。


こちらはiOS版ですが、初回起動するとカードをかざすように求められるのでNFCリーダーで読み取らせます。

こちらが初期状態なので、PINと指紋を登録していきます。
ちなみにSign-in Dataに38leftとあるように38サイト分のサインインデータの登録ができるようです。

こちらが指紋登録をしているところですが、iPhoneのリーダーにカードをかざして指紋にタップを繰り返すことで指紋が登録されます。


パスキーとして登録する

カードのセットアップが終わったら、普通にパスキーとして使えるようになっています。
いつものwebauthn.ioで試してみます。

いつも通り適当にユーザ名を入れてregisterからキー登録を行います。
ただし、デフォルトだとFaceIDが優先されてしまうので、Advanced SettingsでRegistration HintsとしてSecurity Keyを設定します。この設定によりセキュリティキーを使った登録が行われます。
この状態でRegisterをタップするとセキュリティキーを使ったパスキーの登録が始まります。

先ほどの指紋登録時と同じくカードをかざして指紋をタップすれば登録が完了します。

ちなみに管理アプリでカードを読み込ませてみるとSign-in Dataにwebauthn.ioのサインインデータが登録されたことがわかります。


サインインする

次にサインインしてみます。Authenticateボタンをタップするとサインインが求められるのでセキュリティキー・NFCを選択、先ほどと同じくカードをかざして指紋をタップします。
これで完了です。
正常にサインインできました。



という感じで簡単ですが試してみました。
セキュリティキーのネックレス問題が起きているので、カード型だと省スペースになるな〜というのと扉の開閉や身分証明書とのハイブリッド利用ができるようになると便利だと思います。


OAuth2.0 Security Best Current Practiceを読んでみる(3)

$
0
0

こんにちは、富士榮です。

前回前々回とOAuth2.0 Security Best Current Practiceについてみてきました。

今回は攻撃パターンと緩和策についてみていきます。18パターンも定義されているので今回は最初の3つを紹介していきます。


攻撃パターンと緩和策

このセクションでは攻撃ごとに緩和策が記載されています。ここまでのベストプラクティス、攻撃モデルを考慮して、自身の実装を分析し該当する攻撃パターンが当てはまる可能性があるなら緩和策を考える、という使い方になると思います。
  • redirect_uriの検証が不十分
    • 基本は完全一致を検証することになりますが、パターンマッチングのロジックによってはエンコードされた値などをうまくハンドリングできていないケースなどもあり得そうです。
    • 認可コードグラント
      • redirect_uriにワイルドカードを使うケースが主に取り上げられています。
      • 例えば、「https://*.somesite.example/*」という値がredirect_uriとして登録されていた場合、「https://attacker.example/.somesite.example」も通してしまう可能性があります
      • また、CNAMEレコードのメンテナンスについても言及されています。使われていないCNAMEレコードのポイント先のURLを攻撃者が取得することで意図しないredirect_uriへ誘導されてしまう可能性があります
    • インプリシットグラント
      • 認可コードグラントで述べた攻撃はインプリシットでも同様に発生する可能性があります
      • さらにインプリシットの場合、Locationヘッダにフラグメントがついていない場合、フラグメントを再付与してしまう問題により被害を大きくする可能性があります。例えばオープンリダイレクトにより転送された先にアクセストークンを付与してしまうことで攻撃者が用意した転送先にアクセストークンを直接的に送信してしまうことが発生します
    • 緩和策
      • これはシンプルにredirect_uriの完全一致を確認する、につきます
        • ※localhostの場合を除きますが
  • リファラーヘッダーを介した資格情報の漏洩
    • 認可コード、stateパラメータ、さらにインプリシットの場合はフラグメントに設定されるアクセストークンが認可リクエスト・認可レスポンスのURIの内容に含まれることからリファラーヘッダを介して攻撃者に情報が知られてしまう可能性があります
    • OAuthクライアントからの漏洩
      • 通常OAuthクライアントは認可サーバからの認可レスポンスを受け取るとクライアント側の画面をレンダリングする訳ですが、そのページ上に例えば攻撃者のページがリンクとして存在していて強制クリックさせたり、iFrame内の広告などとして埋め込まれていると、リファラーヘッダーを介して認可コード、state、場合によってはアクセストークンなどが意図せずに漏洩します
    • 認可サーバからの漏洩
      • 認可サーバから漏れるような状態になっているとどうしようもない気もしますが、上記と同じように攻撃者のサイトへ誘導される仕組みがあると資格情報が漏洩します
    • 漏洩の結果として、認可コードによるアクセストークンの取得や、アクセストークンそのものの搾取による攻撃が成立してしまいます
    • 緩和策
      • OAuthの認可レスポンスの結果レンダリングされるページや、認可エンドポイントにサードパーティのリソースや外部サイトへのリンクを含まないようにする(まぁ、当たり前ですがたまに認可エンドポイントに広告載せたい事業者とかGAタグを埋め込む事業者いますよね・・・)
      • さらに安全にするには、
        • リファラーヘッダを抑制するリファラーポリシーを適用する
        • 認可コードグラントを利用する(インプリシットを使わない)
        • PKCEを使う
        • 認可コードは一度限りの利用にする(トークンエンドポイントに渡された段階で無効化する)
        • stateを一度限りの利用にする(リダイレクト先で評価された段階で無効化する)
        • 認可レスポンスをリダイレクトではなくform_postを利用する(repsonse_modeパラメータ)
  • ブラウザ履歴による資格情報の漏洩
    • 上記のリファラーヘッダと類似ですが、認可コードやアクセストークンがブラウザの履歴に残るケースが考えられます(これ、攻撃ではありませんがログイン画面をブックマークしちゃう人もいるんですよね・・・)
    • 認可コード
      • redirect_uriへの認可レスポンスが履歴に残るとcode=xxxの部分も残ってしまうことがあり、この認可コードを再利用されることがあります
      • 対策としては、リファラーヘッダのケースと類似ですが、
        • 認可コードを一度限りの利用にする
        • response_modeとしてform_postを利用する
      • となります
    • アクセストークン
      • こちらも前述のものと同じですが、フラグメントだけでなくクエリパラメータでアクセストークンを渡すケースがあり、ブラウザ履歴に情報の残ることがあります
      • 対策としては、
        • クエリパラメータでアクセストークンを渡さない
        • response_modeとしてform_postを利用する
      • となります

とりあえず今回はここまでです。
当たり前に聞こえますが広告とかGAタグは割と普通に実装されている気がするので十分に注意しましょう。







European Identity & Cloud Conferenceのアジェンダが公開されています

$
0
0

こんにちは、富士榮です。

先日紹介したアイデンティティ関連の重要イベントの一つであるEuropean Identity and Cloud Conference 2024のアジェンダが公開されています。

6月4日〜7日の日程で結構朝から夜までセッションが詰め込まれています。

https://www.kuppingercole.com/events/eic2024/agenda


ということで気が早いですが見どころ(私見)を。

初日(6月4日)

  • 10:30- EIC24 Decentralized ID Deployment Bootcamp
    • DIFのKimのセッションです。これから実装プロジェクトをやる人には興味深い内容になっていそうです。
  • 13:20- Vision 2030: Rethinking Digital Identity in the Era of AI and Decentralization
    • Martin Kuppingerのキーノートです。タイトルだけでもワクワクします。AIと分散の中でデジタルアイデンティティはどうなっていくんでしょうか。
  • 17:30- Building Trust in a World of Misinformation and Crisis: Navicgating the Storm
    • Pam Dingleらによるパネルです。日本でもフェイクニュースなどの偽情報対策は喫緊の課題となっており、Trusted Web推進協議会やOriginator Profile CIPなどが活動を進めている領域とも繋がるはずです。
  • 18:00- Consent is Dead
    • Eveのセッションです。SAMLの次はConsentのようです。Venn FactoryっていうのもEve姐さんらしくて最高です。
  • 19:20- Les Miserables of the Cyber Frontier: The Dueling Narratives of Decentralized Identities
    • NatさんとMarkusのキーノートです。分散型IDの世界におけるプライバシーの話を含めレ・ミゼラブルを題材に考えていけそうです。

2日目(6月5日)

  • 11:00- Decentralized Identity Comes of Age: How Identity Forces Are Making Enterprises Rethink Identity
    • となりのトラックのDecentralized Identity & eIDAS 2.0のトラックも気になるところですが、身近なところから分散型IDを考えることも非常に重要です。
    • しかしトラック名にDecentralized IdentiyとeIDAS 2.0をくっつけているのはすごいですね
  • 11:00- Multi-Stakeholder Cross-Border Reusable/Decentralized Identity
    • Meecoの方とDNPの岡本さんのセッションです。隣のセッションと悩ましいところですが応援しに行きたいと思います
  • 12:00- EU Digital Identity: Pilot Projects for the EUDI Wallet
    • eIDASのLarge Scale Pilotの中のEUDIWの取り組みの紹介です。これは必見。
  • 14:30- Digital Wallets and Decentralized Identities: Impact for Business and How to Get on the Brandwagon
    • ID Verificationのソリューション提供者の目線でみたeIDASや分散型IDの文脈がどう見えているのかが聞けると嬉しいですね
  • 14:45- Real-World Examples of How Smart Wallets will Transform how we Navigate our Digital World
    • IATAとAccentureからの事例セッションです。こういう事例はとても大切だと思います
  • 15:10- Fostering Trust in Global Automotive Supply Chains Through DID and SSI
    • 若干前のセッションと時間が被っているので迷う部分ですが自動車業界のサプライチェーンにおいてDIDとSSIがどのように役に立つのか?について日本においても重要なアジェンダになりそうです
  • 15:30- A Practical Guide for EUDI-Wallet Use Case Implementation for Organizations
    • Lissiの方からのユースケース紹介ですね。いつまで経ってもDIWについてはユースケースが・・・という話ばかりなので色々なユースケースを聞いて想像を膨らませるのは非常に重要です
  • 15:30- Wallet Security Mechanisms for the Decentralized Ecosystem
    • 上のセッションと並行しているので悩ましいところですが、DIWを実装する上で重要なセキュリティの観点についてPaulが語ってくれます。実装者は必見になりそうです
  • 15:50- Business Models for Decentralized Identity & EUDIW
    • 先にもありましたが結局のところビジネスにならないと盛り上がらない世界なので分散型IDやWalletを提供している方々がどのように考えているのかは興味深いです
  • 18:10- OpenID AuthZEN: Standards for Modern Authorization
    • 先日のOpenID Hybrid Workshopでも紹介されましたが最近OpenID Foundationが立ち上げた新しいWGの活動です。認可問題は根深いのでキャッチアップしておきたいところです

3日目(6月6日)

  • 11:00- The Wallets we Want
    • Kristinaも参加するセッションですね。まさに私たちが欲しいと思うWalletってなんだろう、というのはそろそろ整理しておきたいところです
  • 12:00- Bridging Borders: Achieving Global Interoperability for Identity and Payment Wallets
    • 後半でGailが出るセッションで語られるSIDI Hubもそうですが、グローバルでの相互運用性がデジタルアイデンティティの大きなアジェンダになってきているのでこのようなセッションは面白そうです
  • 14:30- The DNA of Digital ID - Enabling Roaming Wallets
    • Nickのセッションです。裏番組がドイツのWalletプロジェクトについてなのでそちらも気になりますが、OIXが最近やっている政府が提供するWalletはどうあるべきか?というような取り組みにも繋がる話が聞けるといいかな、と思います
  • 15:10- Securing the Foundations of Verifiable Credential
    • Danielのセッションです。先日紹介したO|D4VCのセキュリティ分析をやった人なのでこのあたりは聞いておきたいですね。ただ裏番組になっているDanielのパネルも気になるところです
  • 16:00- Prepare for G20 with this Digital Identity Sprint & Interactive Survey by SIDI Hub
    • Gailのセッションです。G20に向けてSIDI Hubが重要な取り組みになりそうですので要注目です
  • 17:50- Addressing Usability Challenges of Digital Identity Wallets
    • Markも出てくるようです。DIWの使い勝手は今後の普及にとって重要な意味を持つはずです

最終日(6月7日)

  • 10:30- Decentralized Identity in Production
    • Wayne、Chrisなどのセッションです。両社ともプロダクションで分散型ID関連のソリューションを提供している企業なのでとても参考になりそうです
  • 11:50- Meeting the Challenges of Securing the EUDI Wallet with a High Level of Assurance: an Overview of Deployment
    • Danielの話もありますが実際にWalletの実装をする際に高いレベルの保証を行うにはどうしたら良いか、という話のようです

どうしても分散型ID中心になりますが、色々とみるべきセッションがたくさんありそうです。
私ですか?私は最終日にVerifiable Credentialsを使ったID保証の話をユースケース中心にお話しする予定でございます。

では、現地でお会いしましょう。

OAuth2.0 Security Best Current Practiceを読んでみる(4)

$
0
0
こんにちは、富士榮です。

引き続きOAuth2.0 Security Best Current Practiceを読んでいきます。

今回も攻撃パターンと緩和策についてみていきます。前回18パターンのうち始めの3つを紹介したので、今回は続きを紹介していきます。

攻撃パターンと緩和策

このセクションでは攻撃ごとに緩和策が記載されています。ここまでのベストプラクティス、攻撃モデルを考慮して、自身の実装を分析し該当する攻撃パターンが当てはまる可能性があるなら緩和策を考える、という使い方になると思います。

  • Mix-Up攻撃
    • 一番有名な攻撃なんじゃないでしょうか。8年前にnovがoauth.jpに詳細をアップしているのでこちらを読むのが良いとおもいます
    • とはいえ本題はBCPなのでこちらを読んでいくと、亜種に関する説明があります
    • Mix-Up with Interception
      • 前提として攻撃者がユーザ(被害者)の認可リクエストとレスポンスを傍受できる環境にあることが設定されています
      • 通常のMix-Upでは攻撃者が用意した認可エンドポイントで正直な認可サーバへリダイレクトしますが、このケースでは正直な認可サーバへのリクエスト〜レスポンスを傍受し、攻撃者の認可サーバへ強制的にリダイレクトします。あとは通常と同じ流れですね
    • インプリシットグラント
      • インプリシットグラントの場合、認可コードではなくアクセストークンを直接受け取るわけですが、受け取ったアクセストークンを攻撃者が用意したuserInfoエンドポイントへ投げ込まさせることで攻撃者がアクセストークンを搾取する、というシナリオです
    • 認可サーバごとのredirect_uri
      • 複数認可サーバが存在するケースにおいてクライアントが選択した認可サーバをセッションに保存せず、redirect_uriの検証を正しく行わない場合、認可サーバとredirect_uriの組み合わせがおかしくなる、というシナリオです
    • OpenID Connect
      • Discoveryのメカニズムを悪用するケースですね。ログイン時にwebfingerでメールアドレスのドメインパートからwell-knownエンドポイントを探してきますが、この際にメールアドレスのドメインを置き換えることで攻撃者のIdPのエンドポイントへ誘導、動的クライアント登録の仕組みでRPを登録させてしまう、その後は通常のMix-Upと同じという感じでしょうか
    • 緩和策
      • そもそも論としてクライアントが単一の認可サーバのみと連携している状態ではMix-Up攻撃は出てきませんので、そのような場合は除外できます
      • 基本的な考え方としては、認可リクエストをどの認可サーバに送信したのかをクライアントがきちんと覚えておくことが最も重要です
      • 認可サーバの識別を行うにはissクレームを使い、きちんと評価する必要があります
      • redirect_uriが複数の認可サーバで共用されてしまうことよる問題を避けるにはクライアントは認可サーバごとにredirect_uri(コールバック先)を分ける必要があります
  • 認可コードのインジェクション
    • これも有名な攻撃ですね。何らかの方法で搾取した認可コードを攻撃者のセッションの中に注入することで不正にアクセストークンの取得をするわけです
    • 具体的には、こんな流れですね
      • 攻撃者は何らかの方法で認可コードを手にいれる
      • 攻撃者は正規のクライアントから認可コードフロー改めて開始する
      • 認可エンドポイントから発行されてくる攻撃者の正しい認可コードを不正に入手した認可コードに置き換える
      • この結果、攻撃者は不正に被害者のアクセストークンを取得できてしまう
    • この攻撃は認可コードを使い捨てにすること、そもそも認可コードを盗まれないようにするためにredirect_uriのチェックをする、などの対策が可能です
    • 緩和策
      • PKCEやnonceを使うのが基本です
  • アクセストークンインジェクション
    • 不正に入手したアクセストークンを利用して別ユーザになりすましてリソースサーバへアクセスができてしまう、という問題です
    • 緩和策
      • 正直OAuthではアクセストークンがトランザクションやUserAgentにバインドされないため、アクセストークンを搾取されたらおしまいではあります
      • OpenID Connectを使う場合はat_hashやnonceを使ってトランザクションの整合性を検証することも大切です

今回も3つばかり紹介しました。
では、またお会いしましょう。

OpenID for Verifiable Credential IssuanceのImplementers Draftに向けた準備

$
0
0

こんにちは、富士榮です。

OpenID for Verifiable Credential IssuanceもImplementer’s Draftに向けたPublic Review期間に入りました、ということを以前のポストで少しだけ触れましたが、更新履歴を見ていきたいと思います。

こちらがOpenID Foundationからの公式アナウンスです。

https://openid.net/review-of-proposed-implementers-draft-of-openid-for-verifiable-credential-issuance/

これを見ると、以下のスケジュールで進むようです。

  • 2/8-3/24 Public Review期間
  • 3/11 投票のアナウンス
  • 3/18 早期投票開始
  • 3/25-4/1 公式投票期間

問題なく進めば4月には正式にImplementer’s Draft 1が出そうですね。


Implementers Draft 1に向けて仕様がどのように更新されたのかを見るにはDocument History(Appendix F)を見るのが一番なのでこちらを見ていきましょう。

https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-13.html#appendix-F

余談ですが、仕様を読む時、Appendixに結構有用な情報(議論されてきた経緯やサンプルなど)があるので是非Appendixも読むと良いと思います。


で、こちらがDocument Historyのうち、今回の更新分です。

さすが、結構多いです。(マーカーを引いた部分が個人的には結構重要な変更だと思うので既存の実装を持っている人は気をつけないと行けなさそうです。主にパラメータ名の変更などです)

  • change the structure of proof_types from an array to a proof_types_supported map that contains a required proof_signing_alg_values_supported parameter
  • renamed cryptographic_suites_supported to credential_signing_alg_values_supported to clarify the purpose of the parameter
  • renamed credential_configurations Credential Offer parameter to credential_configuration_ids
  • remove format from the Credential Response
  • added signed_metadata parameter
  • clarified that logo can is a uri and not a url only
  • moved the annex with Credential format profiles to the top of all annexes
  • added a Notification Endpoint used by the Wallet to notify the Credential Issuer of certain events for issued Credentials
  • completed IANA registrations section
  • clarified description of a mandatory claim
  • made sure to use gender-neutral language throughout the specification
  • added an option in authorization_details to use credential_configuration_id pointing to the name of a credential_configurations_supported object in the Credential Issuer's Metadata; in addition to an option to use format and type.
  • renamed credentials Credential Offer parameter to credential_configuration_ids
  • renamed credentials_supported Credential Issuer metadata parameter to credential_configurations_supported
  • grouped credential_encryption_jwkcredential_response_encryption_alg and credential_response_encryption_enc from Credential Request into a single credential_response_encryption object
  • replaced user_pin_required in Credential Offer with a tx_code object that also now contains description and length
  • reworked flow description in Overview section
  • removed Credential Offer examples from Credential format profiles
  • added support for HTTP Accept-Language Header in the request for Credential Issuer Metadata to request a subset for display data
  • clarified how the Credential Issuer indicates that it requires proof of possession of the cryptographic key material in the Credential Request
  • added an option to use data integrity proofs as proof of possession of the cryptographic key material in the Credential Request
  • added privacy considerations
  • clarifed that AS that only supports pre-auth grant can omit response_types_supported metadata
  • added background_image credential issuer metadata
  • editorial clean-up (fix capitalization, etc.)


そろそろちゃんと実装初めていっても良さそうな時期にきましたね。

パスキーの実装をし始めてみる

$
0
0

こんにちは、富士榮です。

OpenID Providerを作ろうシリーズについてはユーザの認証をどうしようかな、、というところで一旦止まっているのですが、やっぱりやるならパスキーかな、ということで寄り道してみます。

と言ってもパスキーの細かいところは実際に実装したことがあるわけではないので、勉強しながら実装していこうと思います。

ということでまずは認証器の登録からです。

constcred=awaitnavigator.credentials.create({
publicKey:options,
});

ってブラウザのAPIを実行するやつです。


まず今回は上記APIを実行するところまでをゴールとしたいと思います。

必要なことは、

  • サーバサイドでチャレンジを生成する
  • 画面でユーザIDを入力させる
  • その他、登録させる認証器の要件などを含むパラメータを生成する
  • APIを実行する

です。


サーバサイドの実装

ということで、サーバ側でチャレンジを生成する処理を書くところからです。最終的にAPIに渡す際にはチャレンジはArrayBufferである必要がある、かつ後続の処理でサーバサイドでチャレンジが生成したものと合致するかどうかを確認する必要もあるのでサーバサイドでArrayBufferの値を生成します。

今回はランダムの値を生成することにしました。

// challengeを生成する
functiongenerateRandomBytes(length) {
constbytes=newUint8Array(length);
for (leti=0; i<length; i++) {
bytes[i] =Math.floor(Math.random() *256);
}
returnbytes;
}

この値をクライアントサイド(ブラウザ上で動作するJS)へ渡してあげる必要があるのでエンドポイントを定義するのと、ArrayBufferのままでは安全に値が渡せないのでbase64urlエンコードする処理を書いてあげます。

// challengeをエンコードして返却する
router.get('/getChallenge', async (req, res) => {
res.send(b64encode(generateRandomBytes(16)));
});

※base64urlエンコードはよくある関数なので割愛します。


これで/getChallengeエンドポイントへGETするとチャレンジを生成してbase64urlエンコードした値が返ってくるようになりました。(実際はrouterで/passkey/getChallengeにマッピングしています)

クライアント側の実装

クライアント側はHTMLとその中に埋め込まれたJavaScriptで構成されます。
UIとしてユーザ名を取得するテキストボックスと登録開始をするボタンを配置しておきます。今回はejsを使っています)
<html>
<head>
<scriptsrc="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>
<inputtype="text"id="userId"/>
<buttonid="createPasskey"onclick="register()">Create Passkey</button>
<scriptsrc="client.js"></script>
<script>
asyncfunctionregister() {
letuserId = $("#userId").val();
try {
awaitregisterCredential(userId);
} catch (e) {
alert(e.message);
console.error(e);
}
}
</script>
</body>
</html>

この中に埋め込まれているclient.jsがパスキー関連の処理を行う本体です。
やることは、画面のテキストボックスに入力されたユーザIDの値を取得して、ボタンを押下するとregister()関数が呼び出され、その中のregisterCredential()関数が呼び出されます。
このregisterCredential()がclient.jsの中に書いてあります。

処理の順番としては、まずはチャレンジ関係の処理をしています。
  • 先ほどのサーバサイドのチャレンジ生成エンドポイントを呼び出してチャレンジを取得する
  • チャレンジがbase64urlエンコードされているのでデコードしてArrayBufferに戻す
// challengeを取得する(後で使うのでサーバサイドで生成する)
constrequestUrl='/passkey/getChallenge';
constrequest=newRequest(requestUrl);
constheaders= {
'X-Requested-With':'XMLHttpRequest'
};
constresponse=awaitfetch(request, {
method:"GET",
credentials:'same-origin',
headers:headers
});
// バイナリを扱うためにサーバ・クライアント間ではbase64urlエンコードした値でやり取りする
constencodedChallenge=awaitresponse.text();
constdecodedChallenge=awaitb64decode(encodedChallenge);

次に画面上で入力したユーザIDを処理します。こちらもArrayBufferである必要があるので、この辺りの関数を使って変換しています。
asyncfunctionstring_to_buffer(src) {
return (newUint16Array([].map.call(src, function(c) {
returnc.charCodeAt(0)
}))).buffer;
}

// 画面に入力された文字列をArrayBufferへ変換する
constarrayBufferUserId=awaitstring_to_buffer(userId);

あとはパスキー登録APIを呼び出すため、上記のチャレンジやユーザIDを含め必要なオプションを生成します。
// パスキー登録のためのパラメータを生成する
constoptions= {
challenge:decodedChallenge,
rp: {
name:"test site",
id:"08d.....-e33c.ngrok-free.app"
},
user: {
id:arrayBufferUserId,
name:userId,
displayName:userId
},
pubKeyCredParams: [
{alg:-7, type:"public-key"},
{alg:-257, type:"public-key"},
{alg:-8, type:"public-key"}
],
excludeCredentials: [],
authenticatorSelection: {
authenticatorAttachment:"platform",
requireResidentKey:true,
userVerification:"preferred"
}
};

細かい意味は次回にでも解説します。
ここまでくるとAPIを呼び出すだけです。
// ブラウザAPIの呼び出し
constcred=awaitnavigator.credentials.create({
publicKey:options,
});


この状態で一度実行してみるとよくみるパスキー登録画面が出てきます。


あとはこの登録レスポンスの値をハンドリングして、バックエンドで保持してあげる形にしていけば登録は終わりです。次は登録周りも実装してみたいと思います。

というわけで今回はここまでです。

パスキー登録APIのオプションを読み解く

$
0
0

こんにちは、富士榮です。

前回に続きパスキーです。

今回はcreate()を呼び出すときのオプションを見てみたいと思います。

constcred=awaitnavigator.credentials.create({
publicKey:options,
});

このoptionsの部分です。

参考にするブラウザAPIのドキュメントはこちらです。

https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create

このうちのcredential typeがpublicKeyとなっているのがパスキーです。

今回実装してみた部分だけピックアップして一覧にしてあります。

パラメータ説明値の例
challenge登録情報の検証を行うためのチャレンジ(ArrayBuffer)[114, 189, 97, 231, ...]
rp
nameRP名test site
idRPのドメイン名example.jp
user
idユーザ識別子(ArrayBuffer)[251, 226, 123, 109,...]
nameユーザ名(登録ダイアログへの表示はこちらがされる)テストユーザ
displayNameユーザの表示名(処理系によってはこちらが表示されるのかも。MacとiOSのSafari/Firefox/Chromeだと表示名は出てこなかった)テストユーザ
pubKeyCredParamRPがサポートしている公開鍵のアルゴリズム
上から順番に利用される
{alg: -7, type:"public-key"} : ES256
{alg: -257, type:"public-key"} : RS256
{alg: -8, type:"public-key"} : Ed25519
{alg: -7, type:"public-key"},
{alg: -257, type:"public-key"},
{alg: -8, type:"public-key"}
excludeCredentials
id登録済みのauthenticatorを除外するために利用。登録済みのauthenticatorのidを指定
type除外するauthrnticatorのタイプpublic-key
transports除外するauthenticatorのI/Fタイプ
internal : 内蔵
usb : USB
nfc : NFC
ble : Bluetooth Low Energy
smart-card : Smart Card
internal
authenticatorSelection
authenticatorAttachmentプラットフォーム組み込みか、クロスプラットフォームかの選択
platform : OS組み込み(TouchIDなど)
cross-platform : クロスプラットフォーム(Yubikeyなど)
platform
requireResidentKey認証器にユーザ情報を登録するかどうか
true
false
true
userVerificationユーザ認証を実施するかどうか
required : 必須
preferred : 可能なら実施
discouraged : 実施しない
preferred


これらのパラメータを画面からある程度指定できるようにしたので、次回以降で各種認証器を使った場合にどういう動きになるのかを確認していきたいと思います。


ちなみに、Macのブラウザで内蔵Touch IDで登録した場合はこんな感じになります。

credentialId、credentialTypeは面白くないので、transportsとflagsあたりが指定するオプションと使った認証器でどう変わるのか、がポイントになりそうです。


ちなみにflagsは7bitのスイッチになっていて、それぞれこんな意味を持ちます。

Bit意味説明
0User Presence (UP)If set (i.e., to 1), the authenticator validated that the user was present through some Test of User Presence (TUP), such as touching a button on the authenticator.
1--
2User Verification (UV)If set, the authenticator verified the actual user through a biometric, PIN, or other method.
3Backup Eligibility (BE)If set, the public key credential source used by the authenticator to generate an assertion is backup-eligible. This means that it can be backed up in some fashion (for example via cloud or local network sync) and as such may become present on an authenticator other than its generating authenticator. Backup-eligible credential sources are therefore also known as multi-device credentials.
4Backup State (BS)If set, the public key credential source is currently backed up (see Bit 3 for context).
5--
6Attested Credential Data (AT)If set, the attested credential data will immediately follow the first 37 bytes of this authenticatorData.
7Extension Data (ED)If set, extension data is present. Extension data will follow attested credential data if it is present, or will immediately follow the first 37 bytes of the authenticatorData if no attested credential data is present.

先ほどの例では、「01011101」なので下位ビットからフラグをみると以下のように読めます。

  • Bit0(User Presence):あり
  • Bit1(-):0
  • Bit2(User Verification):あり
  • Bit3(Backup Eligibility):あり
  • Bit4(Backup State):あり
  • Bit5(-):0
  • Bit6(Attestation Credential Data):あり
  • Bit7(Extension Data):なし

って感じになっています。つまり、ユーザが介在していることの確認(タップなど)がされ、整体やPINでユーザ検証が行われ、クラウドで同期されているパスキーである、ということですね。

さまざまな認証器でパスキー登録APIの返却値を確認する

$
0
0

こんにちは、富士榮です。

OpenID Providerをパスキー対応にするためにパスキーの実装をしているわけですが、登録時のnavigator.credentials.createのAPIのレスポンスを各種認証器で確認してみました。


写真)どんどん増えていく認証器たち


色々と解せない点(黄色で色付けしたセル)も出てきているのですが、実際にパスキーの実装をする際は色々なデバイス・ブラウザ・認証器の組み合わせで試験をしないといけないのでこういうチェックはしないといけないと思います。

利用
デバイス
ブラウザ
認証器
ユーザ認証
レスポンス
TransportUPUVBEBSATED
Mac
Safari
内蔵Touch ID指紋Internal, hybrid111110
Yubikey USB-C/NFCPINnfc, usb110010
Yubikey USB-C/LightningPINusb110010
Yubikey USB-A/NFC
(PIN未設定)
-userVerificationをrequiredにしているのにPIN設定が求められず、登録できてしまう。ただしUVは0となる
eWBM GoldenGate USB-CPINusb110010
iPhoneFaceIDInternal, hybrid111110
Android指紋Internal, hybrid111110
Chrome
内蔵Touch ID指紋Internal, hybrid111110
Yubikey USB-C/NFCPINnfc, usb110010
Yubikey USB-C/LightningPINusb110010
Yubikey USB-A/NFC
(PIN未設定)
-userVerificationをdiscouragedにしてもPIN設定が求められる
eWBM GoldenGate USB-CPINoperation not allowed
iPhoneFaceIDInternal, hybrid111110
Android指紋Internal, hybrid111110
Firefox
内蔵Touch ID指紋internal111110
Yubikey USB-C/NFCPINnull110010
Yubikey USB-C/LightningPINnull110010
Yubikey USB-A/NFC
(PIN未設定)
-userVerificationをrequiredにしているのにPIN設定が求められず、登録できてしまう。ただしUVは0となる
eWBM GoldenGate USB-CPINnull110010
iPhoneFaceIDinternal111110
Android指紋internal111110
iPhone
Safari
FaceIDFaceIDInternal, hybrid111110
Yubikey USB-C/NFCPINnfc, usb110010
Yubikey USB-A/NFC
(PIN未設定)
-userVerificationをrequiredにしているのにPIN設定が求められず、登録できてしまう。ただしUVは0となる
Authntrend AT-Key/NFC指紋nfc110010
Android指紋Internal, hybrid111110
Chrome
FaceIDFaceIDInternal, hybrid111110
Yubikey USB-C/NFCPINnfc, usb110010
Yubikey USB-A/NFC
(PIN未設定)
-userVerificationをrequiredにしているのにPIN設定が求められず、登録できてしまう。ただしUVは0となる
Authntrend AT-Key/NFC指紋nfc110010
Android指紋Internal, hybrid111110
Android
Chrome
OSアンロック指紋Internal, hybrid111110
Yubikey USB-C/NFCPINble, hybrid, internal, nfc, usb110010
Yubikey USB-C/LightningPINble, hybrid, internal, nfc, usb110010
Yubikey USB-A/NFC
(PIN未設定)
-userVerificationをrequiredにするとPINが求められ、discouragedにするとPIN設定が求められず、UVが0で登録される
eWBM GoldenGate USB-CPINble, hybrid, internal, nfc, usb110010


ポイントとしては、

  • Firefoxを使うとTransportが上手く取れない
  • AndroidのChromeではcross-platform認証器のTransportがおかしい
  • userVerificationをtrueにセットしてPIN未設定のYubikeyを使うと本来はPIN設定が求められるべきだと思うがそのまま登録ができてしまうケースがある

などあるので、結局はちゃんとflagsの値を見て期待通りの認証器の状態となっているかを確認しないといけない、、ということです。



OAuth2.0 Security Best Current Practiceを読んでみる(5)

$
0
0

こんにちは、富士榮です。

すこし空きましたがこちらも続けていきます。

引き続き攻撃パターンと緩和策です。前回はアクセストークンインジェクションまで行きましたので続き7個目/18個のクロスサイトリクエストの偽造から行きたいと思います。


攻撃パターンと緩和策

  • CSRF(クロスサイトリクエストフォージェリ)
    • 攻撃者としては正規のクライアントが攻撃者の制御下にあるリソースにアクセスさせたいので、redirect_uriに不正にリクエストをインジェクションしようとします
    • 緩和策
      • 基本はstate/nonce/PKCEを正しくセッションに紐づけた形で利用することにつきます
        • しかしながら例えば、クライアントがPKCEを使う場合は当然のことながら認可サーバがPKCEをサポートしていることを確認しないといけません
        • 同じくstateを使う場合はstateの改ざんやスワッピングに対する耐性を持つような実装にしないと意味がありません
      • 認可サーバは自身がPKCEをサポートしていることをクライアントが検知するための仕組みを提供する必要があります(MUST)
      • 基本はメタデータを使うことになりますが、別のメカニズムで検知方法を提供しても問題はありません(MAY)
      • stateやnonce(response_typeがid_tokenの場合)は認可レスポンスやトークンレスポンスを攻撃者が読み取れる環境においてはリプレイアタックなどに使われる可能性がありますが、その点はPKCEを使うことで対応ができます
  • PKCEダウングレード攻撃
    • PKCEをサポートしているものの、全てのフローがPKCE対応しているわけではない認可サーバはPKCEダウングレード攻撃を受ける可能性があります
    • 例えばこんな実装です
      • 認可リクエストにcode_challengeがあったらPKCEを有効にする、という判定ロジックが認可サーバに組み込まれている(逆にいうとcode_challengeが指定されない場合はPKCE対応しない)
      • 上記の前提があるにも関わらずCSRF対策としてstateを使わない(PKCEを使うことを前提としてしまっている)
    • まぁ、当然ですがこうなるとCSRF対策をしていないのと同じです
    • 攻撃者はクライアントと認可サーバの間に入り、code_challengeを丸ごと削除してしまいセッションを乗っ取るわけです
    • 緩和策
      • この攻撃を受けている際の特徴は、認可リクエストにcode_challengeがない(削除されている)にも関わらずトークンエンドポイントへのアクセス時はcode_verifierが指定されることにあります
      • code_challengeが削除されていることを検知するために認可サーバは認可コードとcode_challengeを紐づけて管理しないといけません。このことによりトークンエンドポイントに認可コードがPOSTされたタイミングでcode_challengeとの紐付けが有効かどうかを検証することが可能となります
      • 加えて認可サーバは、認可エンドポイントへのリクエストにcode_challengeが指定されなかったにも関わらずトークンエンドポイントへcode_verifierが指定された場合はリクエストを拒否しないといけません(MUST)
  • リソースサーバでのアクセストークンの漏洩
    • リソースサーバが偽造されている場合(アクセストークンフィッシング)
      • 基本はredirect_uri(リソースサーバ)とクライアントの紐付きがゆるいケースにおいて発生します
    • リソースサーバが侵害されている場合
      • ログの盗難やシステムの完全な掌握までさまざまなパターンがありますが、リソースサーバが侵害されるとアクセストークンが盗難されてしまいます。当たり前ですが
    • 緩和策
      • 基本的にSender Contraintトークンを使うという対策に尽きます
      • 同じくAudience Restrictionも大切な対策です
      • またこれも原則ですがリソースサーバでアクセストークンは他の機密情報と同じようにPlain textで保存したり他のシステムへ転送するなどは避け厳重に扱う必要があります

今回も3つ紹介しました。
まだあと半分くらいありますね。。

パスキー登録APIのレスポンスを解析する

$
0
0

こんにちは、富士榮です。

先日、いろいろな認証器でパスキー登録をしてみましたが、レスポンスの中のフラグをどうやって取得するの?というあたりについて細かく解説していないの今回解説していきたいと思います。

navigator.credentials.create()メソッドの返却値の定義がこちらのドキュメントにあります。

https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create#return_value_2

定義によると、PublicKeyCredentialが返されるようですね。

A Promise that resolves with an PublicKeyCredential instance matching the provided parameters. If no credential object can be created, the promise resolves with null.

では、PuiblicKeyCredentialの定義を見ていきましょう。

https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential

以下のメンバが含まれます。

  • PublicKeyCredential.authenticatorAttachment
  • PublicKeyCredential.id
  • PublicKeyCredential.rawId
  • PublicKeyCredential.response
  • PublicKeyCredential.type
上記のうち、PublicKeyCredential.responseの中に各種フラグなどが格納されます。
このresponseはAuthenticatorAttestationResponse(AuthenticatorResponseから継承)にはgetAuthenticatorData()というメソッドがあり、認証器のデータを取得できます。
このメソッドは最低37バイトからなるArrayBufferであるAuthenticator dataという形式のデータを返却します。
回りくどかったですが、このAuthenticator Dataの中に欲しい情報が入っています。
先に書いた通り、この返却値はArrayBufferで、
  • rpIdHash (32 bytes)
  • flags (1 bytes)
  • signCount (4 bytes)
  • attestedCredentialData (variable length)
  • extensions (variable length)
という構造になっています。

先日のポストではこの33バイト目にあるflagsの値を見ていたわけです。
flagsの値を再掲しますが、この1バイトの中の各ビットが認証器が使用されたときの状態を表しています。
Bit意味説明
0User Presence (UP)If set (i.e., to 1), the authenticator validated that the user was present through some Test of User Presence (TUP), such as touching a button on the authenticator.
1--
2User Verification (UV)If set, the authenticator verified the actual user through a biometric, PIN, or other method.
3Backup Eligibility (BE)If set, the public key credential source used by the authenticator to generate an assertion is backup-eligible. This means that it can be backed up in some fashion (for example via cloud or local network sync) and as such may become present on an authenticator other than its generating authenticator. Backup-eligible credential sources are therefore also known as multi-device credentials.
4Backup State (BS)If set, the public key credential source is currently backed up (see Bit 3 for context).
5--
6Attested Credential Data (AT)If set, the attested credential data will immediately follow the first 37 bytes of this authenticatorData.
7Extension Data (ED)If set, extension data is present. Extension data will follow attested credential data if it is present, or will immediately follow the first 37 bytes of the authenticatorData if no attested credential data is present.


この辺りをコードで表すとこんな感じになります。

まずはcreate()を行います。
// ブラウザAPIの呼び出し
constcred=awaitnavigator.credentials.create({
publicKey:options,
});

フラグの値を取得します。
constflags = newDataView(cred.response.getAuthenticatorData()).getUint8(32).toString();
$("#userPresence").text("User Presence(UP) : "+ ((Number(flags) & 1)? 'Yes': 'No'));
$("#userVerification").text("User Verification(UV) : "+ ((Number(flags) & 4)? 'Yes': 'No'));
$("#backupEligibility").text("Backup Eligibility(BE) : "+ ((Number(flags) & 8)? 'Yes': 'No'));
$("#backupState").text("Backup State(BS) : "+ ((Number(flags) & 16)? 'Yes': 'No'));
$("#attestedCredentialData").text("Attested Credential Data(AT) : "+ ((Number(flags) & 64)? 'Yes': 'No'));
$("#extensionData").text("Extension Data(ED) : "+ ((Number(flags) & 128)? 'Yes': 'No'));

getUint8の引数で32バイトオフセットすることでflagsのところまで辿りつけるので、その値をNumber化した上で各ビット単位で論理和をとって状態を取得しています。

ざっとこんな感じでフラグを取得していました、という種明かしでした。









パスキーの登録レスポンスの検証を行う

$
0
0
こんにちは、富士榮です。

最近はパスキーの実装を眺めているわけですが、そろそろ認証器の登録を行う段階まで来ました。(まだまだ完成までは遠い道のりです。でも全然触ってこなかったものを勉強しながら作っていくのは楽しいですね)

これまでのポストはこちらです。

これまでの流れでパスキーを登録する流れを見てきました。
  • サーバのエンドポイントからchallengeを取得する
  • ブラウザAPIにchallenge、ユーザ情報、Relying Party情報、認証器への要求事項をセットして実行する
  • 認証器をアクティブ化する(Touch IDをタッチする、など)
  • ブラウザAPIからの返却値を検証し、認証器を登録する
という流れで認証器の登録を行います。
今回は最後のステップである「ブラウザAPIからの返却値を検証し、認証器を登録する」ところに入っていきます。

// ブラウザAPIの呼び出し
constcred=awaitnavigator.credentials.create({
publicKey:options,
});

こんな感じでnavigator.credentials.create()を実行すると、PublicKeyCredentialが返されます。(上記例ではcred)
PublicKeyCredentialの仕様は以下のドキュメントに記載されています。

そして、これが今回の最大のテーマでもありますが、安全に認証器を登録するためには以下のポイントを押さえておく必要があります。
  • 登録を開始したセッションから最終的に認証器を登録するまでのセッションの一貫性
  • フィッシングやMITM等により攻撃者の認証器を横から登録されることの防止
この辺りはOpenID Connectとも共通しますが、セッションと紐づけた状態でchallenge(OpenID Connectの場合はstateやnonce)をサーバ側とクライアント側で引き回すことで一連の流れの中で認証器を登録していることを保証します。

そのためにPublicKeyCredentialの中に含まれる以下の値の検証を行うことが大切となります。
  • challenge
    • 認証器登録を開始する際にサーバ側で生成し、セッションに紐づけておきます。
    • credential.create()のオプションに取得したchallengeを入れることで登録しようとしている認証器とchallengeの紐付けを行います
    • 認証器がアクティブ化されcredential.create()からの返却値には認証器から受け取ったchallengeの値が入ります
    • credential.create()の返却値をサーバ側へ渡し、サーバ側に認証器を登録します。この際、最初に生成しセッションに紐づけたchallengeと同じ値が認証器の登録結果から取得したものと同一かどうかを確認します
  • origin
    • 認証器を提示した先のサイトと、認証器を登録する先のエンドポイントが同一のホストであることを確認することでフィッシングなどを防ぎます
    • credential.create()の返却値に含まれるclientJSONのメンバにoriginの値が入っているため、この値と認証器を登録するエンドポイントのホスト名が一致しているかどうかの検証が大切です。
  • rpIdHash
    • これoriginと同じ様にユーザがパスキーを登録しようとしているRelyingParty(credential.create()を実行する際のオプションに含まれます)と実際に認証器を登録するRelyingParty(credential.create()の結果のclientJSONに含まれます)が同一であることが必要です。

では実際のコードを見ていきましょう。

Challenge

まずはchallengeです。ポイントはセッションと紐づけてchallenge値を保持しておくことです。
router.get('/getChallenge', async (req, res) => {
constchallenge=b64encode(generateRandomBytes(16));
console.log("save challenge into session : "+challenge);
req.session.challenge=challenge;
res.send(challenge);
});

今回の実装では/getChallengeがGETされるとランダム値を持つchallengeを生成しています。APIの仕様としてArrayBufferとする必要がありますが、ブラウザとバックエンドの間でやり取りするのにバイナリではやりにくいのでbase64urlエンコードをして扱います。なお、この値をブラウザに返すと同時にセッションにも保存をしておきます。この保存した値を使って後から登録処理の一貫性を担保します。

ブラウザ側はchallenge(base64urlエンコード済み)を受け取るとcredential.create()の引数となるoptionのメンバにArrayBufferに戻したchallengeをセットしてAPIを実行します。
// バイナリを扱うためにサーバ・クライアント間ではbase64urlエンコードした値でやり取りする
constencodedChallenge=awaitchallenge.text();
constdecodedChallenge=awaitb64decode(encodedChallenge);

こんな感じでoptionを生成していきます。
// パスキー登録のためのパラメータを生成する
constoptions= {
challenge:decodedChallenge,
rp: {
name:"test site",
id:window.location.hostname
},
user: {
id:arrayBufferUserId,

このオプションを指定してcredential.create()を実行して得られるレスポンス(以下の例ではcred)をサーバ側へPOSTします。
constsavedCredential=await_fetch(
'/passkey/registerPasskey',
'POST',
{
'Content-Type':'application/json'
},
JSON.stringify(cred)
);

このPublicKeyCredential形式のcredの中にはclientDataJSONが含まれ、その中にchallengeの値が入ってきます。
// navigator.credentials.create()から返却されるclientDataJSONの取得
constclientJSON=JSON.parse(base64url.decode(req.body.response.clientDataJSON))
サーバ側では受け取った値を解析し、clientDataJSONの中からchallengeの値を取得し、セッションに保持していたchallengeの値と比較します。
console.log("expected challenge from session : "+req.session.challenge);
console.log("challenge to be evaluated : "+clientJSON.challenge);
if(req.session.challenge!==clientJSON.challenge){
console.log("challenge mismatch");

origin

次はoriginです。
こちらも同じくclientDataJSONに含まれるのでサーバ側のドメインとoriginの値が一致しているか比較します。
// originがアクセスされているURLと同じかどうか
// ngrokでテストしているのでhttpではなくhttpsで固定する(localhost以外の場合)
constscheme=req.hostname!=='localhost'?"https":req.protocol;
constorigin=url.format({
protocol:scheme,
host:req.get('host')
});
console.log("expected origin : "+origin);
console.log("origin to be evaluated : "+clientJSON.origin);
if(origin!==clientJSON.origin){
console.log("origin mismatch");

rpIDHash

最後はrpIdHashです。名前の通りrpIdのハッシュ値です。この値はclientDataJSONの中ではなく、同じくPublicKeyCredentialのresponseの中にあるauthenticatorDataに格納されています。
このauthenticatorDataは先日のflagの判別にも使いましたが、最初の32バイトがrpIdHash、33バイト目がflag、という形で構成されるバイナリ値です。そのため、authenticatorDataの最初の32バイトを切り出して比較可能な形式に変換する必要があります。

一方で比較対象となるrpIdはその名の通りあらかじめセットしてRelying PartyのID(通常はホスト名)のハッシュ値です。ハッシュの仕様の説明はこちらにある通りでSHA256です。ちゃんと検証しなさい、ということも書いてあります。
The SHA-256 hash of the Relying Party ID that the credential is scoped to. The server will ensure that this hash matches the SHA256 hash of its own relying party ID in order to prevent phishing or other man-in-the-middle attacks.
ということで仕組みが分かりましたので、まずは比較対象となるrpIdのハッシュを計算します。
constrpId=req.hostname;
constexpectedRpHash=crypto.createHash('sha256').update(rpId).digest('hex');
console.log("expected rpId hash: "+expectedRpHash);

こんな感じの文字列が取得できます。
4dc265ac185ff67ead15d60a5cc745c2f10cd367cea3493a79a38a5f7a040a8b

次にauthenticatorDataの中から比較するものを取得します。
constdecodedAuthenticatorData=base64url.toBuffer(JSON.stringify(req.body.response.authenticatorData));
constrpHashBuffer=decodedAuthenticatorData.slice(0, 32);
constrpHashString=Buffer.from(newUint8Array(rpHashBuffer)).toString("hex");
console.log("rpId hash to be evaluated : "+rpHashString);

やっていることはsliceで最初の32バイトを切り取ります。
この状態だとArrayBufferなのでという状態のデータとなり、先に生成したrpIdHashとの比較を単純に行うことはできません。
<Buffer 4d c2 65 ac 18 5f f6 7e ad 15 d6 0a 5c c7 45 c2 f1 0c d3 67 ce a3 49 3a 79 a3 8a 5f 7a 04 0a 8b>

ただ、値を見ると上記で生成した4dc2....と同じ値になっていることはわかります。
これを文字列比較可能な状態にするためにArrayBufferを16進数の文字列へ変換(toString('hex'))します。
これで文字列として値の比較ができる様になりますので、challengeやoriginと同じ様に比較をしていきます。

もちろんプロダクション環境では実績のあるライブラリを使ってやる処理なのですが、今回は処理の内容を理解するためにこの辺りの細かいところも実装してみました。

比較がうまくいったら認証器の情報をサーバ側に保存して処理は終了です。
この辺りは次回以降で説明したいと思います。

デジタル認証アプリがやってくる(その後)

$
0
0

こんにちは、富士榮です。

先月、デジタル認証アプリに関連する法令に関するパブコメ募集が出ている件について個人的に考える課題点について書きました。

デジタル認証アプリがやってくる

https://idmlab.eidentity.jp/2024/01/blog-post_28.html 


それなりのPVがあったこともあり、某雑誌社の方からインタビューがあったりもしました。

パブコメの募集が2月末までだったこともあり、コメントをしてみました。

(なお、重要なことですがこの分野は素人なので頓珍漢なコメントをしている可能性も高いです。後述しますが今回のパブコメの対象は認証アプリそのものというよりも法律だったこともあり、私の条文の解釈の仕方はおそらく間違っている可能性が高いです)

ということで個人的解釈に基づく今回のパブコメ募集の中身を深掘りしていきたいと思います。

パブコメの対象は何か? 

はい、ここがおそらく一番重要だと思います。
募集要項にバッチリ書かれています。よく読んでコメントしましょう。
対象は「法令施行規則」ですね。

読んでみます。縦書きPDF・・・


その中のコメントの対象は何か?

対象が法令施行規則だということが分かった上でコメントしてほしいポイントはどこなのか?という点を見ていきましょう。
「命令案」の中に「改正後」として記載されているところに改正したいポイントがわかる様に記載されています。

ここは優しく「概要」という資料に改正のポイントがまとめられていますのでこちらも併せて見ると理解が早まると思います。


要するに、「電子署名等確認業務受託者」に「内閣総理大臣」を加えたいということですね。
なんでこんなことをやっているのか?を紐解くためには「電子署名等確認業務受託者」とはなんなのか、どういう義務を負うのか、について改正対象となる「電子署名等に係る地方公共団体情報システム機構の認証業務に関する法律」を見てみましょう。
こちらで参照できます。

これをみていると結局のところ「電子署名等確認業務受託者」、つまり現状でいう「プラットフォーム事業者」といわれるJ-LISのサーバに対してアクセスをしてマイナンバーカードの有効性確認等を実施することができる事業者に「個人番号カード用利用者証明用電子証明書」に関して「利用者に関する情報を適切に扱うこと」などを義務付ける法律であることがわかります。


では、なぜ内閣総理大臣を受託事業者に追加するのか?

今回やりたいことは認証アプリの「システム連携イメージ」に記載がある様に、デジタル庁の管理する「デジタル認証アプリサーバ」がJ-LISの管理するJPKIサーバに対してアクセスする、ということです。これは正に現在のプラットフォーム事業者が行なっているマイナンバーカードの有効性確認などと同じ構図となります。

これまでは前述の法律に則って総務省認定を受けた受託事業者のみがJPKIサーバへのアクセスを許可されていたわけですが、上記の図の構成、つまりデジタル庁がJPKIサーバへアクセスしようと思うと根拠法が存在しないわけです。

となると、行政機関の長である内閣総理大臣を受託事業者に加えておかないといけなくなる、とうロジックなんだと思います。


本当にそれでいいのか?どうコメントすべきか?

構図がわかったところでツッコミどころコメントすべき事項を探すわけですが、結局は従来の民間のプラットフォーム事業者と行政機関の差を紐解いていくのがアプローチが良いのではないかと思います。

利用者に関する情報を適正にあつかうこと、という点については民間だろうが行政機関だろうがそうでしょうね、という感じで違和感はないのですが、この利用者に関する情報は一体何を含むんだろうか?という点を見ていくとどうやら法律では「利用者証明利用者符号」を中心に考えているんじゃないかな?と思えてきます。

結局、マイナンバー(カードじゃなく)や証明書シリアルなど、背番号制度に関するアレルギーが意味不明に強い日本では符号や識別子に関しては非常に慎重に扱われる傾向にありますが、行政機関においては識別子についてはそもそも扱う前提があるんじゃなかったっけ?民間事業者が扱うことになるから認定制度があるんじゃなかったっけ?というところに行き着きます。

むしろ前回のポストでも記載した通り行政機関がやるから気持ち悪いポイントは公共・準公共・民間という異なるコンテキストを横断的に政府のIdentity Providerがまとめてフェデレーションをする、つまりデジタル庁の認証アプリサーバから見て利用者がどのRelying PartyとID連携しているのかがわかってしまう、という点にあるはずです。これはIdentity Providerを実装したことがある人ならわかると思いますが、利用者がどのRelying Partyに対して属性提供について同意しているかの状態を保持したり、PPIDを生成するための連携状態を保持したり、とログを含むとそれなりにID連携状態の情報を持つ必要が出てきてしまいます。

こうなってくるとこの法令施行規則に関する改正のポイントが受託事業者の対象に内閣総理大臣を加えることでプラットフォーム事業者と同じことをデジタル庁ができる様になりますよ、だけでは少々不足していると思われるので、法令改正対象には少なくとも「符号などに関する情報の適正な管理」だけでなく「ログを含むID連携状態の適正な管理」を求めていかないといけないのではないか?というのが現時点での私の結論です。

とりあえずそんな主旨でコメントはしてみたので、今後どうなっていくかは注視していきたいと思います。








パスキーの登録を行う

$
0
0
こんにちは、富士榮です。

前回まででパスキーの登録に必要な最低限の準備ができたので公開鍵の情報をユーザに紐づけて保存していきましょう。

現時点で最低限登録すべき情報は、
  • ユーザ情報(ユーザ名、ユーザID)
  • 認証器のID(credentialId)
  • 公開鍵(PublicKey)
  • 署名回数(signCount)
くらいです。
今後、同一ユーザが複数の認証器を登録するケースへの対応はしていくとして、現時点では認証器は一人一つの登録に限定しておきたいと思います。(単に面倒なだけです)

では初めていきましょう。

ユーザの情報を取得する

前回までは認証器にフォーカスしてきたのでユーザ名、ユーザIDはフォームからの入力とAPI呼び出しの際に都度作成するだけでどこにも保存してきませんでした。
しかし、今回はパスキーの情報をユーザ情報と紐づけて保存しなければならないので、
  • 認証器の登録開始前にユーザ名入力〜ユーザIDの生成 → セッションに保存
  • 認証の生成API実行
  • API返却値の取得〜登録 → セッションからユーザ情報を取り出して一緒に登録
ということをする必要があります。

ということで、クライアント側のJSで認証器登録オプションを生成する際に、サーバ側のエンドポイントを呼び出して画面から入力したユーザ名の保存とArrayBuffer形式でのユーザIDの生成の処理をサーバ側に移します。
こんな感じの処理になります。
(クライアント側のJS)
// ユーザIDを取得する
// ユーザを登録する
constuser= {
username:userId
};
constuserIdresponse=await_fetch(
'/passkey/user',
'POST',
{
'Content-Type':'application/json'
},
JSON.stringify(user)
);
constencodedUserId=awaituserIdresponse.text();
constarrayBufferUserId=awaitb64decode(encodedUserId);


(サーバ側のJS)
// ユーザを登録する
router.post('/user', async (req, res) => {
// 登録状態を調べる
// 既存なら登録済みパスキーを取得して返却する→Excludeに入れる
// 新規なら登録
// 構造
// - username
// - userId
// userIdを生成する
userId=base64url.encode(generateRandomBytes(16));
constuser= {
username:req.body.username,
id:userId
};
// ユーザ情報をセッションに保存する
req.session.user=JSON.stringify(user);
// userId(ArrayBuffer)を返却する
res.send(userId);
})

見ての通り、クライアント側ではフォームに入力したユーザ名(userId)をサーバ側にPOST、サーバ側ではArrayBuffer形式でランダムなユーザID(同じ変数名にして失敗しましたがuserId)を生成、セッションに保存した上でクライアントにエンコード済みのユーザIDを返却しています。
クライアントはこの値をbase64urlデコードした上でoptionに指定してcredential.create()を実行することになります。

認証器の登録結果を取得する

これは単純にcredential.create()の結果を取得するだけです。
// ブラウザAPIの呼び出し
constcred=awaitnavigator.credentials.create({
publicKey:options,
});

このcredという変数ですね。ここは前回まででも話しました。
この中身を保存していく必要があるのでサーバ側にこの値を渡していくことになるのですが、このままだとバイナリの値を含むのでサーバ側で取り回しにくいためPublicKeyCredentialのインターフェイスにはtoJSON()というメソッドがあるため、これを使ってJSON化したものをサーバ側へPOSTしていきます。
// 取得した認証器情報をサーバ側で保存
constsavedCredential=await_fetch(
'/passkey/registerPasskey',
'POST',
{
'Content-Type':'application/json'
},
JSON.stringify(cred.toJSON())
);

サーバ側では前回のポストで紹介した様にchallenge、origin、rpIdHashの検証を行います。
そしていよいよ保存すべき情報をPOSTされてきた情報から取得していきます。

先に述べた通り、保存すべき情報は以下の通りです。
  • ユーザ情報(ユーザ名、ユーザID)
  • 認証器のID(credentialId)
  • 公開鍵(PublicKey)
  • 署名回数(signCount)
ユーザ情報は先ほどセッションに入れておいたものを取り出せばOKです。
// sessionからユーザ情報を取り出す
constuser=JSON.parse(req.session.user);
console.log("username: "+user.username);
console.log("userid: "+user.id);

認証器のIDはPOSTされてきたJSON(PublicKeyCredential)の直下にidという要素で入っていますので取り出します。
// credentialIdをPublicKeyCredentialから取得する
constcredentialId=req.body.id;
console.log("credential id: "+credentialId);

公開鍵はJSONの下のresponseの中に入っているので取り出します。
// publicKey(base64urlエンコード済み)を取得する
constpublicKey=req.body.response.publicKey
console.log("public key: "+publicKey);

署名回数はフラグの次の4バイトに入っているのでrpIdHashと同じくauthenticatorDataの中から取り出します。最初の32バイトがrpIdHash、33バイト目がフラグ、34バイト目〜4バイト分が署名回数ですので、0オリジンでsliceして値を取り出します。当然値はArrayBufferなので適切に処理します。今回はヘキサの文字列にしたのちにParseIntで数値化しています。
// signCount
constsignCount=decodedAuthenticatorData.slice(33,37);
constsignCountHex=Buffer.from(newUint8Array(signCount)).toString('hex');
console.log("sign Count: "+signCountHex);
console.log("sign Count Number : "+parseInt(signCountHex, 16));

これで登録に必要な情報は揃いました。
// 登録する情報
constpasskey= {
username:user.username,
userId:user.id,
credentialId:credentialId,
publicKey:publicKey,
signCount:parseInt(signCountHex, 16)
}

あとは以前OpenID Providerを作る時にも使ったJSONBinを使いパスキーの情報を登録していきます。
// JSONBinへ登録
constJSONBinResponse=awaitjsonbin.registerPasskey(JSON.stringify(passkey));

exports.registerPasskey=asyncfunction(passkey) {
constpasskeyJSON=JSON.parse(passkey);
console.log("username: "+passkeyJSON.username);

constheaders=newHeaders({
"X-Master-Key":process.env.JSONBIN_MASTER_KEY,
"X-Collection-Id":process.env.JSONBIN_PASSKEYCOLLECTION_ID,
"X-Bin-Name":passkeyJSON.username,
"Content-Type":"application/json"
});
constbinUrl=newURL(`${process.env.JSONBIN_BASEURL}/b`);
constbinResponse=awaitfetch(binUrl, {
method:'POST',
headers:headers,
body:passkey
});
returnawaitbinResponse.json();
}

これで登録はできましたね。

これでようやくサインイン処理の実装に進む準備ができましたので、次回以降でサインイン処理を実装していきたいと思います。


登録済みのパスキーを削除する(MacOS)

$
0
0
こんにちは、富士榮です。

パスキーの実装をしていると大量のパスキーが実装中のRelying Partyに対して登録されてしまうことがあります。(使っているユーザ名を適当に使っていると尚更、、です)

こんなことが起きます。(ユーザ名が大量に表示されてしまいます)
これを消すにはどうしたら良いか、という話ですがMacの場合はシステム設定のパスワードメニューを開き、Relying PartyのID(ドメイン名)を入れると登録されているパスキーが大量に出てくるので個別に確認して消していきましょう。
ちなみに複数選択して一括で削除もできます。





実装とテストは計画的に、、、、ですね。

パスキーでログインを実装する(クレデンシャルの取得編)

$
0
0

こんにちは、富士榮です。

前回までで登録がある程度実装できたので、細かいところを実装する前にログイン側の処理を実装してみましょう。

その前に、これまでのポストはこちらです。


登録に比べてログインは割とシンプルです。

  • 登録と同じ様にサーバ側で生成したchallengeを含むoptionを生成する
  • optionを指定してnavigator.credential.get()を実行する
  • 認証器をアクティブ化し、navigator.credential.get()の返却値から取得できるCredentialIDをキーに登録済みの公開鍵を取得する
  • 取得した公開鍵を使い同じくnavigator.credential.get()の返却地に含まれるデジタル署名等の検証を行い、成功したら認証OKとする
こんな流れです。


では実装していきましょう。

Challengeの取得

登録の時と同じですね。

// ログインする
asyncfunctionloginWithPasskey(userId, userVerification) {
// challengeを取得する(後で使うのでサーバサイドで生成する)
constchallenge=await_fetch(
'/passkey/getChallenge',
'GET',
{
'X-Requested-With':'XMLHttpRequest'
}
);
// バイナリを扱うためにサーバ・クライアント間ではbase64urlエンコードした値でやり取りする
constencodedChallenge=awaitchallenge.text();
constdecodedChallenge=awaitb64decode(encodedChallenge);

サーバ側で生成してbase64urlエンコードされたchallengeをクライアント側でデコードしてArrayBufferの形にしてあげます。


optionパラメータを生成する

取得したchallengeを含むoptionパラメータを生成します。ユーザ認証を認証器側に求めるかどうかのスイッチもここで設定できます。

// パスキーログインのためのパラメータを生成する
constoptions= {
challenge:decodedChallenge,
allowCredentials: [],
userVerification:userVerification
}


navigator.credential.get()を実行する

生成したoptionを指定してAPIを実行します。

constcred=awaitnavigator.credentials.get({
publicKey:options,
mediation:'optional'
});

画面にはパスキーダイアログが表示されます。



結果、返却されるPublicKeyCredentialの中身を見ると今度は特徴的なプロパティとして

  • userHandle
  • signature
が入っていることがわかります。


このuserHandleはパスキー登録時に指定したユーザIDなので、credentialIdをキーにデータベースを検索して公開鍵を取得、このユーザIDに対して発行されたものなのかどうかの確認する、という流れになります。

JSONBinの中のcredentialIdが一致しているものを探してPublicKeyを取得します。



あとは検証の部分ですので、次回解説していきたいと思います。

PlayStationでパスキーを使う

$
0
0

こんにちは、富士榮です。

そういえばPlayStation Network(PSN)がパスキーに対応したというニュースが1月末ごろに流れたので確認してみていたのですが、その時点ではUSアカウントしかサポートしていない?みたいな状態だったのでしばらく放置していましたが、先ほど確認してみたら私のアカウントでもパスキーが使えるようになっていました。

ポイントはこちらです。

  • パスキーのサポート(とりあえずブラウザからのログインで確認)
  • パスキーを有効にするとパスワードは無効化される
  • パスキーを無効化する際にパスワードの生成を求められる
  • パスキーの識別はUser-Agentが使われる
  • パスキーを無くした際はメール+秘密の質問もしくは生年月日で回復できる
  • 回復の際はパスワードを生成が求められ、パスキーは無効化される

まぁ利便性などを考えると仕方ないのかもしれませんが、メール到達性+生年月日で回復できてしまうのは微妙だなぁ、、と思いつつ。


実機(といってもPS4しか持っていない)での確認は次回以降でやりたいと思いますが、とりあえずブラウザでの設定関係についてまとめておきます。


パスキーの登録

マイページにログインしてセキュリティーの設定の中に「パスキーでサインイン」というメニューがあります。


編集をクリックして登録を開始していきます。

とりあえずTouchIDで生成していきます。

生成が完了するとログアウトされます。


サインイン

サインイン画面に行くと自動的に候補となるパスキーが表示されます。

このまま選択してTouchIDをアクティベートするとログインが完了します。

なお、Autofillを無視してサインインIDにメールアドレスを入力して次へ、をクリックすると「パスキーでサインイン」しか出てこずパスワードでのログインはできないようになっています。


先ほどのマイページのセキュリティ設定を見るとパスキーが有効、パスワードが無効になっていることがわかります。



パスキーを無効化する

この状態でパスキーを無効化してみます。



パスワードの生成を求められますので、ここで生成するとパスキーが無効になり、パスワードでログインができるようになります。


パスキーの管理

パスキーの管理メニューを開くと登録済みのパスキーの一覧が出てきます。

パスキーの識別にはUser-Agentが使われるようです。自分で名前がつけられる方が親切な気はしますがまぁいいかと思います。


ローカル認証器が使えない状態でのサインイン(ハイブリッド)

メールアドレスを入力しパスキーでサインインから他端末で読ませるQRを表示する方法もありますが、リカバリの流れで他端末を使ったサインインもサポートしています。


リカバリ

他の端末を含め全部ロストしてしまったケース用の動線も用意されています。

具体的にはアカウント回復用のメール+秘密の質問もしくは生年月日を使います。


画面下部の「アカウントの回復用のEメールを送信」をクリックするとメールが送られてくるのでリンクをクリックすると回復用の画面が表示されます。


秘密の質問への回答を覚えている訳がないので生年月日を選びます。

検証に成功するとパスワードの生成を求められます。

この段階でパスキーが無効になりますので、パスワードでログインした後に再度パスキーを登録する必要があります。


なお、パスキーが無効化された状態でパスキーでログインしようとするとエラーが表示されます。



とりあえずはブラウザで試しましたが実機を触る時間ができたら実機でも試してみたいと思います。














Viewing all 769 articles
Browse latest View live