2024年2月19日月曜日

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

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

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

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

const cred = await navigator.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でユーザ検証が行われ、クラウドで同期されているパスキーである、ということですね。

2024年2月18日日曜日

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

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

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

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

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

const cred = await navigator.credentials.create({
publicKey: options,
});

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


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

必要なことは、

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

です。


サーバサイドの実装

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

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

// challengeを生成する
function generateRandomBytes(length) {
const bytes = new Uint8Array(length);
for (let i = 0; i < length; i++) {
bytes[i] = Math.floor(Math.random() * 256);
}
return bytes;
}

この値をクライアントサイド(ブラウザ上で動作する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>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>
<input type="text" id="userId" />
<button id="createPasskey" onclick="register()">Create Passkey</button>
<script src="client.js"></script>
<script>
async function register() {
let userId = $("#userId").val();
try {
await registerCredential(userId);
} catch (e) {
alert(e.message);
console.error(e);
}
}
</script>
</body>
</html>

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

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

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

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

あとはパスキー登録APIを呼び出すため、上記のチャレンジやユーザIDを含め必要なオプションを生成します。
// パスキー登録のためのパラメータを生成する
const options = {
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の呼び出し
const cred = await navigator.credentials.create({
publicKey: options,
});


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


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

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

2024年2月17日土曜日

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

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

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.)


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

2024年2月16日金曜日

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

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

引き続き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つばかり紹介しました。
では、またお会いしましょう。

2024年2月15日木曜日

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

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

先日紹介したアイデンティティ関連の重要イベントの一つである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保証の話をユースケース中心にお話しする予定でございます。

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

2024年2月14日水曜日

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

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

前回前々回と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タグは割と普通に実装されている気がするので十分に注意しましょう。







2024年2月13日火曜日

Authentrendのカード型のPasskeyを試す

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

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

昨年末の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を選択、先ほどと同じくカードをかざして指紋をタップします。
これで完了です。
正常にサインインできました。



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