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

非機能系を中心にログイン時の処理について考えてみる

$
0
0

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

OpenID Providerを作っていくとメインの機能ではないけど必要な属性情報を扱う必要性に気がついてきます。

例えば、最終ログイン日付とか利用しているOSやブラウザの種別などの環境要素なども代表的なものの一つだと思います。

これらの情報をどうやってユーザデータベースに保存するか、そしてこれらの情報をどのタイミングで読み書きするのか、がID基盤の性能やセキュリティ、スケーラビリティなどのいわゆる非機能系の設計を行う上では重要な要素になりそうです。今回はそれらの属性の取り扱いを含むログイン処理の実装について考えてみたいと思います。


非機能系で利用できそうな属性とは?

ちょっとこの辺を考えてみましょう。

例えばこんな区分の属性が考えられるかもしれません。

属性区分属性の例用途
環境情報
アクセス元IPアドレス地域によるアクセス制限、前回と異なる環境からのアクセスの検知(リスク要素)
利用ブラウザ前回と異なる環境からのアクセスの検知(リスク要素)、トラブル時の原因解析
利用OS前回と異なる環境からのアクセスの検知(リスク要素)、トラブル時の原因解析
アカウント情報
作成日時捨てアカウントの検知、トラブル時の原因解析
パスワード更新日時トラブル時の原因解析、アタック時の対応、移行・切り替え時の対応
ロック、削除状態トラブル時の原因解析
利用した認証手段前回と異なる環境からのアクセスの検知(リスク要素)、トラブル時の原因解析
最終ログイン日付トラブル時の原因解析、アタック時の対応、移行・切り替え時の対応


通常のメールアドレスや名前などのユーザ情報とは異なりますが、ユーザを認証する際のなりすましリスクの判定やトラブル時の原因解析などを考えるとこれらの情報は重要になる場面が容易に想定できます。(もちろんこれらの情報以外にも利用できる属性はたくさんあると思います)


これらの属性情報の扱いを含めログイン処理はどのように実装するべきか?

では、単純に上記の情報をアクセスの都度ユーザデータベースに保存していけば良いのか?というとこれはこれで考えるポイントがありそうです。

一般に認証(ログイン)処理は出来うる限りシンプルにしていくことが望ましいと考えられます。これは可用性や性能の観点から複雑な処理を入れるとログイン処理が遅くなったり同時に多数のユーザがアクセスする場合にサーバーリソースを多く消費してしまうことが考えられるためです。

ちなみにこれはMicrosoft Azure AD B2Cの例ですが、ユーザデータベースから属性情報を取得するオペレーションとユーザデータベースへ属性を書き込むオペレーションでは処理にかかる時間が倍以上異なる(当然書き込みの方が倍以上遅い)ということが(あくまで個人の経験として)わかっています。これは他の処理系を使っても同様の傾向にあるはずです。

また、加えてログイン時の条件や認証結果によってログイン処理にかかる時間があまりに異なると内部処理の推測をされてしまうなど攻撃者によって大きなヒントを与えてしまうことにもなりえます。(例えば、削除済みユーザ、ロック済みユーザとパスワードを単に間違えたユーザで認証試行に対するレスポンス時間が極端に異なると、ブルートフォースアタックをされた時にユーザが存在する可能性がわかってしまう、などのリスクに繋がります)

そう考えると、少なくともログイン処理では、入力された識別子をキーにデータベースを検索したあと、

  1. 以下の処理の時間を同じくらいの時間がかかるようにウェイトをかける
  • エントリが存在しなかった場合
  • エントリが存在するが
    • 提供されたクレデンシャルで認証ができなかった場合
    • ロックアウトなどにより認証をブロックする場
  1. ログイン成功の場合は環境属性を非同期で書き込む(キューインフチェーンパターンなどの利用)
  2. など実装上の工夫をするべきだと言えると思います。

    スクラッチでOpenID Providerを作って商用で利用することはそれほどないとは思いますが、この辺りのことも考えて実装されたID基盤を使うことでセキュアでスケーラビリティの高いシステムを構築することができるようになると思います。



    OpenID Providerを作る)scopeの定義と返却する属性

    $
    0
    0

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

    積み残した実装を徐々に進めていきましょう。

    その前にこれまでのおさらいです。


    今回はscopeです。
    OpenID Connect coreの仕様の該当箇所ではscopeと対応する属性について、このように定義されています。(簡略化しています)
    scope属性形式備考
    profile
    namestring
    family_namestring
    given_namestring
    middle_namestring
    nick_namestring
    preffered_usernamestring
    profilestringURL
    picturestringURL
    websitestringURL
    genderstringfemale/maleを利用
    birthdatestringISO 8601:2004形式(YYYY-MM-DD)
    zoneinfostringIANAタイムゾーン形式(JapanやAmerica/Los_Angelesなど)
    localestringBCP47言語タグ表現(ja_JP、en_USなど)
    updated_atnumberunixtime
    email
    emailstring
    email_verifiedboolean検証済みならtrue
    address
    formattedstring
    JSON
    street_addressstring
    localitystring
    regionstring
    postal_codestring
    countrystring
    phone
    phone_numberstringE.164形式
    phone_number_verifiedboolean検証済みならtrue

    なお、上記とは別にOpenID ProviderをOAuth2.0の認可サーバと兼ねる場合でOpenID Connectプロトコルを使う場合は「openid」scopeの指定が必須となります。


    例によってユーザ情報はハードコードしていますが、こんな感じの処理になるはずです。
    /utils/user.js
    exports.getUserIdentity=function(scopes) {
    // ユーザデータの定義
    letuserIdentity= {
    local_identifier:"test",
    // profile scope
    name:"taro test",
    given_name:"taro",
    family_name:"test",
    middle_name:"",
    nickname:"",
    preferred_username:"test@example.jp",
    profile:"https://twitter.com/phr_eidentity",
    picture:"https://1.gravatar.com/avatar/25eee85430bd0bbdcb9cff75655afa43cc9f69bc8730aec852d8538179646ef1",
    website:"hhtps://idmlab.eidentity.jp",
    gender:"male",
    birthdate:"1900-01-01",
    zoneinfo:"Japan",
    locale:"jp_JP",
    updated_at:1704034800,
    // email scope
    email:"test@example.jp",
    email_verified:true,
    // address scope
    address: {
    formatted:"Kokyogaien, Chiyoda-ku, Tokyo 1000002 JAPAN",
    street_address:"Kokyogaien",
    locality:"Chiyoda-ku",
    region:"Tokyo",
    postal_code:"1000002",
    country:"JP"
    },
    // phone scope
    phone_number:"+81-3-1234-5678",
    phone_number_verified:true
    }
    // スコープによって返却する属性の絞り込み
    if(!scopes.includes("profile")){
    deleteuserIdentity.name;
    deleteuserIdentity.given_name;
    deleteuserIdentity.family_name;
    deleteuserIdentity.middle_name;
    deleteuserIdentity.nickname;
    deleteuserIdentity.preferred_username;
    deleteuserIdentity.profile;
    deleteuserIdentity.picture;
    deleteuserIdentity.website;
    deleteuserIdentity.gender;
    deleteuserIdentity.birthdate;
    deleteuserIdentity.zoneinfo;
    deleteuserIdentity.locale;
    deleteuserIdentity.updated_at
    };
    if(!scopes.includes("email")){
    deleteuserIdentity.email;
    deleteuserIdentity.email_verified;
    };
    if(!scopes.includes("address")){
    deleteuserIdentity.address;
    };
    if(!scopes.includes("phone")){
    deleteuserIdentity.phone_number;
    deleteuserIdentity.phone_number_verified;
    };
    returnuserIdentity;
    }

    scopeによってユーザ情報から必要な値のみを返すようにしています。

    また、前回のPairwise識別子の実装と合わせてid_tokenのペイロードを作る処理は上記をコールする形で以下のように実装しています。
    /oauth2/oauth2.js
    //
    // scope関連の処理
    //
    // scopeの判断
    constscopes=req.query.scope.split("");
    // 本来はscopeにopenidが入っていない場合はエラーとする(仕様上はopenidが含まれない場合の動作は未定義)
    // scopeに応じたユーザの情報を取得する
    letpayload=userIdentity.getUserIdentity(scopes);
    // Pairwise識別子の生成
    constPPID=utils.createPPID(payload.local_identifier, req.query.redirect_uri);
    // ローカル識別子の削除
    deletepayload.local_identifier;
    // PPIDをsubとして設定
    payload.sub=PPID;

    これで例えば、openid、address、phoneを指定するとこんな感じでid_tokenが帰ってきます。
    http://localhost:3000/oauth2/authorize?scope=openid%20address%20phone&response_type=id_token&client_id=111&redirect_uri=https://jwt.ms&state=hoge


    必要な属性だけがid_tokenに含まれていることがわかります。

    また、今回対応するscopeと属性を増やしたのでメタデータ(/discovery.js)も更新しておきましょう。
    constscopes= ["openid", "profile", "email", "address", "phone"];

    constclaims_openid= ["sub", "iss", "aud", "exp", "iat", "nonce", "c_hash", "at_hash"];
    constclaims_profile= ["name", "family_name", "given_name", "middle_name", "nick_name", "preffered_username", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale", "updated_at"];
    constclaims_email= ["email", "email_verified"];
    constclaims_address= ["address"];
    constclaims_phone= ["phone_number", "phone_number_verified"];
    constclaims=claims_openid.concat(claims_profile, claims_email, claims_address, claims_phone);

    今回のUpdateを含むコードはこちらにあげてありますので参考にしてください。


    OpenID Providerを作る)定義済み属性の値として何を返却すべきか

    $
    0
    0

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

    前回はscopeの定義と返却すべき属性について整理をしました。

    ちなみに仕様の5章に定義されているStandard ClaimsはIANAのレジストリに登録されているのでOpenID ProviderとRelying Partyの間で個別にフォーマットや値のすり合わせをしなくても良いように標準化されています。登録済みのClaimについては18章に記載されています。

    ですので、OpenID Providerを作る時は、基本的にこのStandard Claimをサポートするようにし不足している属性はRelying Partyと個別に調整を行った上で定義する必要があります。

    今回はStandard Claimsとして実際にどのような値を返すべきか確認していきましょう。


    その前にこれまでのおさらいです。


    では、早速確認していきます。
    昨日のStandard Claims一覧を再掲しておきます。
    scope属性形式備考
    profile
    namestring
    family_namestring
    given_namestring
    middle_namestring
    nick_namestring
    preffered_usernamestring
    profilestringURL
    picturestringURL
    websitestringURL
    genderstringfemale/maleを利用
    birthdatestringISO 8601:2004形式(YYYY-MM-DD)
    zoneinfostringIANAタイムゾーン形式(JapanやAmerica/Los_Angelesなど)
    localestringBCP47言語タグ表現(ja_JP、en_USなど)
    updated_atnumberunixtime
    email
    emailstring
    email_verifiedboolean検証済みならtrue
    address
    formattedstring
    JSON
    street_addressstring
    localitystring
    regionstring
    postal_codestring
    countrystring
    phone
    phone_numberstringE.164形式
    phone_number_verifiedboolean検証済みならtrue

    nameなどのstring形式の属性についてはあまり迷うことはないと思うので、迷いそうなところだけピックアップしておきたいと思います。
    • profile
      • この属性にはURLを返却することが想定されています。仕様をみると「この Web ページに掲載されるコンテンツはこの End-User に関するものであるべきである (SHOULD).」とあるので、もしOpenID Providerがマイページやプロフィールページを持っているならそのURLなどを使うと良いと思います。
    • picture
      • この属性も同じくURLですが、このような注意点が定義されています。
        • この URL は画像ファイル (PNG, JPEG, GIF 画像ファイル等) を参照すること (MUST). またこの画像は End-User が撮影した任意の写真ではなく, End-User に言及する際の表示に適切なプロフィール画像とするべきである (SHOULD).
      • 利用者自身でアップロードできたる編集するものじゃなさそうですが、OpenID Providerの種類(コンシューマ向けなのかエンタープライズ向けなのか)でどのような写真を使うのかは変わってきそうです
    • website
      • この属性もURLです。こちらも以下の注意点が記載されています。
        • この Web ページは End-User 自身や End-User が所属する組織が発信する情報を含むべきである (SHOULD).
      • こちらもシナリオ次第ですね
    • email
      • 想像通りだと思いますがメールアドレスのSyntax(RFC 5322)に従う必要があります
    • gender
      • string形式ですが使用上はmale/femaleが定義されています。しかしながら最近のID基盤ではそれ以外の値を定義することもあります。仕様にはその他の値を設定することも許容しています
        • 定義済の値に適切なものがない場合, その他の値を利用してもよい (MAY).
    • birthdate
      • こちらもstring形式ですがISO 8601:2004に準拠すべきです。仕様には以下の記載があります。
        • ISO 8601:2004 YYYY-MM-DD 形式で表現される. 生年を 0000 とすることで生年を省略することもできる (MAY). 生年のみを提示する場合は YYYY 形式としても良い. 利用するプラットフォームの日付関連の関数の実装によって, 生年のみを提供した場合の月日の扱いは様々であるため, 実装者はこの点を考慮にいれて日付を処理すべきである.
    • zoneinfo
      • こちらもstring形式です。ユーザのタイムゾーンを表す属性ですので、IANAのタイムゾーンレジストリに登録されているコードを使うことが想定されています。
      • 例えば日本ならJapan、北米/ロサンゼルスならAmerica/Los_Angelesです
    • locale
      • こちらもstring形式ですが、結構鬼門だと思っています。仕様には以下の記載があります
        • BCP47言語タグ表現. これは通常 ISO 639-1 Alpha-2 言語コードを小文字表記, ISO 3166-1 Alpha-2国コードを大文字表記し, ダッシュでつなげたものである. (en-US, fr-CA 等) 実装によってはダッシュの代わりにアンダースコアを区切り文字に用いる場合もあるため, 互換性の観点からは注意すること. (en_US 等) 
      • いわゆるISOのAlpha-2言語コード(jaとかen)とAlpha-2区にコード(jpとかus)を大文字表記したものをダッシュ(ハイフン)もしくはアンダースコアで繋げたものという緩めの仕様なので、ja-JPもしくはja_JPという形で値が表現されます。この辺りはRelying Partyに対してどのような表現になるのか事前に伝達しておく必要があります
    • phone_number
      • こちらもstring形式ですが、locale同様に少し揺れる要素があります。原則はE.164形式を推奨するようですが、内線番号の有無や区切り文字やカッコの有無などは考慮が必要です
        • この Claim のフォーマットとしては E.164を推奨する (RECOMMENDED). (+1 (425) 555-1212, +56 (2) 687 2400 等) 電話番号が拡張を含む場合, その拡張は RFC 3966 拡張シンタックスで表記することを推奨する (RECOMMENDED). (+1 (604) 555-1234;ext=5678 等)
      • E.164の仕様だけ見ていると+[国番号]に市外局番の最初の0をとったものと局番をつなげたものということですが、実装を見ているとハイフンの有無やカッコの有無が揺れているようにも見えます。この辺りも事前にRelying Partyに伝達しておいた方が良さそうです
    • updated_at
      • こちらはnumberとして定義されていますので要注意です。UTC 1970年1月1日の0:00:00からの経過秒数、いわゆるunix timeをセットする必要があります

    今日はこのくらいにしておきたいと思います。




    OpenID Providerを作る)トークンエンドポイントにクライアント認証を実装する

    $
    0
    0

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

    そろそろトークンエンドポイントに手を入れていきましょう。今回はクライアントの登録状態の確認と認証を行ってみます。このあたりから登録情報を持つ必要が出てきます。(といっても まだデータベースを使うまでもないのでjsonファイルを使っていきます)

    その前にこれまでのおさらいです。


    クライアントに関する情報として何を管理すべきか

    今回の実装においてはクライアント認証さえできればいいので最低限client_idとclient_secretがあれば問題ありませんが、本来はclient_idと紐付けて管理されるべき情報としてredirect_uriや同意画面を出そうと思うとクライアント名やクライアントに関する説明やロゴ画像、利用規約などの情報も必要になりますし、登録状態の管理も行おうと思うと登録日や更新日、有効・無効などのステータス、管理者の連絡先などの情報も必要になってくると思います。

    といってもそこまで必要になるのはもう少し先の話なので、まずは最低限+αということで名称、ID、シークレット、redirect_uriをファイルに保存しておきます。なお、当然複数のクライアントを登録できるようにするためJSON配列でデータを保存しておきます。
    /database/clients.json
    [
    {
    "client_id": "123",
    "client_secret": "secret",
    "redirect_uris": [
    "https://jwt.ms",
    "http://localhost:3000/cb"
    ]
    },
    {
    "client_id": "456",
    "client_secret": "secret",
    "redirect_uris": [
    "https://rp.example.jp/cb",
    "https://rp.example.com/cb"
    ]
    }
    ]

    redirect_uriも複数登録できる必要があるので配列にしています。(今回は利用しませんが)

    クライアント認証方式を決定する

    仕様の9章にclient_authenticationの定義があります。
    • client_secret_basic
      • いわゆるBASIC認証を行う
    • client_secret_post
      • リクエストのBodyにclient_idとclient_secretを入れて送信する
    • client_secret_jwt
      • client_assertionパラメータにJWTを入れて送信する。署名アルゴリズムはHMAC
    • private_key_jwt
      • client_secret_jwtとの違いは署名に秘密鍵を利用すること
    • none
      • クライアント認証を行わない
    ベーシックな実装では基本的にclient_secret_basicとclient_secret_postくらいを実装しておけば問題ないので、まずはこの2つを実装しておきます。

    クライアント認証を実装する

    対象はタイトルの通りトークンエンドポイントなので、当該のエンドポイントにexpress-basic-authを使っても良かったのですが、client_secret_postもサポートするためにミドルウェアを使わずに実装します。といってもAuthorizationヘッダに"BASIC client_id(base64エンコード):client_secret(base64エンコード)”という形で値が渡ってきているだけなので、パースしてデコードするだけです。
    今回は簡易実装なのでAuthorizationヘッダがあればclient_secret_basic、そうでなければclient_secret_postとして判別しています。
    // - クライアントの認証
    letclient_id, client_secret;
    if(typeofreq.headers.authorization==="undefined"){
    // client_secret_post
    client_id=req.body.client_id;
    client_secret=req.body.client_secret;
    }else{
    // client_secret_basic
    constb64auth=req.headers.authorization.split("")[1];
    [ client_id, client_secret ] =Buffer.from(b64auth, "base64").toString().split(":");
    }

    ちなみにclient_secret_postの場合はそのままclient_idとclient_secretがボディに入ってくるだけなので値を取得しています。

    次は認証処理です。といっても先のjsonファイル内のclient_id/client_secretとマッチしているかどうかを確認するだけです。
    // クライアント情報の読み取り
    constclients=JSON.parse(fs.readFileSync(path.resolve(__dirname, "../../database/clients.json")));
    // クライアント登録状態の確認
    constclient=clients.find(i=>i.client_id===client_id);
    if(typeofclient==="undefined"){
    // クライアントが未登録
    res.statusCode=400;
    res.json({
    errorMessage:"client not found"
    });
    }else{
    // クライアント登録確認、シークレットの検証
    if(client.client_secret!==client_secret){
    // クライアント認証エラー
    res.statusCode=400;
    res.json({
    errorMessage:"client authentication was failed"
    });
    }else{
    // クライアント認証成功

    シンプルです。
    ファイルを読み込んで、client_idで情報を検索、登録されているclient_secretの値を比較するだけです。

    ということでこれでトークンエンドポイントのクライアント認証が実装できました。

    client_secret_basicの認証エラーです。

    client_secret_postの認証エラー(未登録)です。


    今回はこんなところです。






    OpenID Providerを作る)認可エンドポイントでクライアントの登録状態を検証する

    $
    0
    0

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

    前回はトークンエンドポイントでクライアント認証を実装したので、今回は認可エンドポイントに戻り、クライアントの登録状態および指定されたredirect_uriが正しいかどうかの検証処理を入れてみます。


    その前にこれまでのおさらいです。


    さて始めていきましょう。
    今回は検証ということでエラー処理に関する仕様を中心に確認していく必要があります。
    ポイントになるのは、この辺りです。

    これらの章から分かることは、以下の2点です。
    • redirect_uriが不正ならOpenID Providerが直接エラーを返し、それ以外の場合はredirect_uriへコールバックすること(関連する記載:Redirection URI が無効でない限り, Authorization Server は適切なエラーと state パラメータで Authorization Request にて指定された Redirection URI に Client を返す.)
    • エラーレスポンスは各フローのredirect_uriへのコールバック方法に従って返却すること(要はコードフローなら&、インプリシット・ハイブリッドなら#で返せ、ということです)

    また、redirect_uriのマッチングルールは認証リクエストに関する仕様に以下のように定められていますので、この辺りも確認ポイントです。
    この URI は, Client が OpenID Provider に対して事前に登録済みの Redirection URI のいずれかと完全一致しなければならない (MUST). マッチングルールは [RFC3986] (Simple String Comparison) の Section 6.2.1 に従うこと.

    要するに完全一致が必要ってことですね。ワイルドカードが使いたい、という要望はよく聞かれますがいわゆるオープンリダイレクトになってしまう可能性があることから仕様上は完全一致が要求されます。 


    それらを踏まえ実装してみます。

    まずはエラーの返却方法がresponse_typeによって異なることからクライアント検証を行う前にresponse_typeを判別しておく必要があります。これは前回のスコープのところで実装したロジックをそのまま使います。

    // エラーの返却方法にも関連するためresponse_typeの判別は最初にやっておく
    // response_typeの取得
    constresponse_types=req.query.response_type.split("");

    次にこれもトークンエンドポイントのところで実装したクライアント情報の取得ロジックを流用して登録状態を判別します。

    // - client_idの登録状態の確認
    // クライアント情報の読み取り
    constclients=JSON.parse(fs.readFileSync(path.resolve(__dirname, "../../database/clients.json")));
    // クライアント登録状態の確認
    constclient=clients.find(i=>i.client_id===req.query.client_id);
    if(typeofclient==="undefined"){
    // クライアントが未登録
    res.redirect(req.query.redirect_uri+errors.errorOnAuthZ(response_types, "invalid_request", "unknown_client_id", req.query.state));
    }else{

    エラーハンドリングは共通処理となるのでこんな感じで外部化しておきます。

    exports.errorOnAuthZ=function(response_types, error_code, error_description, state) {
    // implicitもしくはHybridを判定するフラグ(フラグメントでレスポンスを返すかどうかの判定)
    letmode;
    if(response_types.includes("token") ||response_types.includes("id_token")){
    // ImplicitもしくはHybridフロー
    mode="#";
    }else{
    // codeフロー
    mode="&";
    }
    return(mode+"error="+error_code+"&error_description="+error_description+"&state="+state);
    }

    次はredirect_uriのマッチングです。ここも登録状態の確認とほぼほぼ類似のロジックですが、未登録のredirect_uriの場合はOpenID Providerから直接エラーを返却するようにしています。

    // redirect_uriがclient設定に合致していることの確認
    if(!client.redirect_uris.includes(req.query.redirect_uri)){
    // redirect_uri未登録
    // redirect_uriが不正なのでOPから直接エラーを返却する
    res.status=400;
    res.json({
    error:"invalid_request",
    error_description:"unknown_redirect_uri",
    state:req.query.state
    })
    }else{


    と、こんなところです。

    ここまでの実装もこちらにプッシュしてありますので参考にしてください。


    OpenID for Verifiable Credentialsの正式セキュリティ分析が完了

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

    先日のOpenID Summitや前日のOpenID Foundation Workshopでも触れられましたが、OpenID for Verifiable Credentialsの正式セキュリティ分析(Formal Security Analysis)が完了した、というニュースが1月18日にリリースされています。


    このセキュリティ分析はドイツのシュトゥットガルト大学によって開発されたモデルを用いていて、これまでもOpenID ConnectやFAPIなどのプロトコルやプロファイルとしての安全性を検証するための用いられてきています。

    特にFAPIが金融グレードのセキュリティを求めるプロファイルに対応するのと同様に、OpenID for Verifiable CredentialsはEUにおけるeIDAS2.0の枠組みの中で採用されるなど、国が発行するデジタル資格証明を安全にやり取りする上で非常に重要なプロトコルとなります。
    そのため、このようなセキュリティ分析とお墨付きは相互運用性に加えて非常に重要な要素となりますね。

    こちらが実際の分析結果のレポートへのリンクです。

    100ページ以上ある詳細な分析なので、全部に目を通すのは難易度が高いですが、ざっくりいうと、以下の4つのシナリオを定義して詳細な分析をしていくという形態をとっています。
    • Proof of Presentation Authentication
    • Proof of Issuance Authentication
    • Proof of Presentation Session Integrity
    • Proof of Issuance Session Integrity
    非常にシンプルで相手方をどのように認証するのか、という話およびSessionの完全性がどのように担保されるのか、という話をVCの発行(Issuance)と提示(Presentation)の両面で分析をかけていくという方法のようです。この辺りは通常のOpenID ConnectにおけるOpenID Provider、Relying Party、User-Agentでもstateやnonce、client_id/secretなどを使って同じことをやっているわけです。

    分析結果を少し紹介します。
    今回はIssuanceに関する指摘です。3点指摘がされています。

    1. Cross-deviceシナリオにおけるIssuance時のカスタムURLスキーム
    • これはOpenID for Verifiable Credentialsに関わらず、ですがQRコードをスマートフォンに読み込ませるなど、PCブラウザと別のデバイスと組み合わせるシナリオにはつきもののカスタムURLスキーム(OID4VCIではopenid-credential-offer://)を使う場合に絶対に発生する課題です。
    • QRコードなどでこのカスタムURLスキーム起動されると、当該のスキームが登録されたネイティブアプリケーションが起動するわけですが、複数のWalletアプリケーションが同一の端末にインストールされている場合は、どのWalletが起動するのかの制御をIssuer側は制御できません。(少なくともiOSの場合は最後にインストールしたアプリ、Androidの場合はユーザが選択したアプリが選ばれる)
    • そうなると例えば悪意のあるアプリが当該カスタムURLスキームを使うと意図しないアプリ事業者へVerifiable Credentialsが渡されてしまうことが想定されます。
    なお、このリスクに対応するためにQRコードを読み取る際にWallet側でPINコードを入力させるなど対策を行うわけですが、悪意のあるWalletだったとしても利用者はPINを入力してしまうことも想定されるため、根本的な解決は難しそうです。この辺りはUIの工夫なども必要な領域のようですね。

    2. 認可コードフローにおける認証攻撃
    • この攻撃は悪意のあるWalletアプリケーションが認可コードフローを用いる場合に発生します。
    • よくある話としてアプリケーションを起動する際にユーザ認証を求め、多くの場合ユーザはなんの疑いもなくログインをしてしまいます。
    • こうなると悪意のあるアプリケーションに認可コードが渡るので(これは先のカスタムURLスキームの場合も含め)、悪意のあるアプリケーションがアクセストークンを取得、結果としてVerifiable Credentialsの発行をされてしまいます。
    この攻撃は悩ましいところですね。アプリ内ブラウザでの認証と外部ブラウザ呼び出し〜カスタムURLスキームでの戻しの区別とかユーザにはつかないと思いますし。。

    3. 事前認可コードフローにおけるセッションの完全性攻撃
    • 攻撃者が事前に認証し、クレデンシャルオファーを入手しておきます。
    • この攻撃者のIDを持つクレデンシャルをフィッシングサイトなどにQRコードとして埋め込んでおき、攻撃対象者の読み込ませることで、攻撃対象者のWalletに攻撃者のIDを持つクレデンシャルを発行することができてしまいます。
    • このクレデンシャル自体は正しく署名されたものなので、このクレデンシャルを用いてアクセスコントロールをしているサイトへ攻撃対象が気づかずに情報をアップロードするなどすると情報漏洩に繋がってしまいます。
    この辺りもなかなか気づきにくいのでしょうが、他のフィッシング対策と合わせて防御していくしかないんじゃないかな、と思います。

    とはいえ、引き続き仕様のアップデートは続いていますし、このフィードバックを受けて仕様のブラッシュアップも進んでいるので今後どうなっていくのかは引き続き要注目です。

    今日はこのくらいにしておきます。
    機会があればVerifiable Presentation側の分析結果も紹介します。



    引き続きOpenID for Verifiable Credentialsの正式セキュリティ分析

    $
    0
    0

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

    前回に引き続きOpenID for Verifiable CredentialsのFormal Security Analysisです。

    前回はIssuanceに関する分析結果を見てきましたが、今回はPresentationに関してみていきましょう。

    Presentationについては2点あります。

    1. Crossデバイスフローにおける認証攻撃

    • modeがdirect_postかつCrossデバイスの場合の話ですね。まぁ一番オーソドックスなフローではあると思いますのでなかなか難しい問題です。
    • 攻撃者がVerifierに対して認証インタラクションを開始し、認証要求をフィッシング等で被害者のWalletに読み込ませることで被害者のWalletからvp_tokenがPOSTされてしまう、という話です。


    前回の分析でもフィッシングとの組み合わせで攻撃が成り立つケースが紹介されていましたので、やはりここはなんらかの対策をしないといけないポイントですね。


    2. セッション完全性攻撃

    • これもカスタムURLスキームの話ですね。ただでさえOAuthやOpenID Connectはステートレスな仕組みの中でトランザクションを成立させるためのセッション維持が難しい課題なのにOID4VCxだとクロスデバイスが入ることで更に難しくなっている印象です。(参考:OID4VPの場合のカスタムURLスキームはopenid4vp://)
    • これも攻撃者のWallet(被害者のスマホにインストールされた悪意のあるWallet)が攻撃者のIDを含むvp_tokenをdirect_postでVerifierへPresentできてしまう、という攻撃です。

    やっぱりEUがやっているようにWalletの認定は大事なテーマなんだなぁ、と思わされる分析でした。

    ということで、一旦セキュリティ分析の話はおしまいです。





    JWTのデコードをターミナル上で行う

    $
    0
    0

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

    皆さんIDトークンなどのJWT(JSON Web Token)の中身を確認するのに何を使っていますか?

    私はOkta(Auth0)が提供しているhttps://jwt.ioやMicrosoftが提供しているhttps://jwt.msをよく使うのですが、いちいちブラウザを立ち上げるのが面倒な場合もありますよね。

    もちろんjwt-cliを使ってコマンドラインで見るのもいいのですが、もうちょっとUIも凝ったものも欲しいよね、というVZ Editorが忘れられない人たちもこの界隈にはいるはずです。


    ということで今回紹介するのはjwt.ioと同じくOkta(Auth0)が提供するjwt-uiです。

    図)githubより


    早速導入してみます。(私はMac環境ですが、Windowsでも使えるみたいです)

    インストール

    brewでインストールするようです。

    brew tap jwt-rs/jwt-ui
    brew install jwt-ui

    これだけです。

    起動

    jwtui

    これだけです。立ち上がりました。

    デコード

    UIを起動した状態でEnterを押下すると入力フィールドにフォーカスが当たるので、デコードするJWTをペーストできるようになります。
    こんな感じで使えます。
    もちろんコマンドラインから直接JWTを引数に指定してもデコードができます。
    % jwtui eyJ・・・・という感じです。
    するとUIが起動してデコードされた状態が表示されます。

    jwt-cliと同じように標準出力にデコードされたものを出力することもできます。
    % jwtui -sn eyJ・・・・という感じで使います。(-sは標準出力への出力、-nは署名検証をしない、というオプションです)

    他にも色々とオプションがあるので使ってみてください。
    • -S, --secret <SECRET> Secret for validating the JWT. Can be text, file path (beginning with @) or base64 encoded string (beginning with b64:) [default: ]
    • -s, --stdout Print to STDOUT instead of starting the CLI in TUI mode
    • -n, --no-verify Do not validate the signature of the JWT when printing to STDOUT.
    • -j, --json Format STDOUT as JSON
    • -t, --tick-rate <TICK_RATE> Set the tick rate (milliseconds): the lower the number the higher the FPS. Must be less than 1000 [default: 250]
    • -h, --help Print help
    • -V, --version Print version







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

    $
    0
    0

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


    噂のデジタル認証アプリですが、パブコメが出てますね。

    電子署名等に係る地方公共団体情報システム機構の認証業務に関する法律施行規則の一部を改正する命令案に対する意見募集について

    https://public-comment.e-gov.go.jp/servlet/Public?CLASSNAME=PCMMSTDETAIL&id=290310311&Mode=0

    2月末までの募集のようなのでぜひ皆さんみておくと良いと思います。

    ざっくり見てみたいと思います。色々と課題もありそうです・・・

    概要

    OpenID Connect/OAuth2.0もちゃんと採用されていますね。


    サービス提供領域

    個人的に一番気になっていた民間PF事業者との棲み分けにについても記載されていますね。

    準公共サービスの範疇をどこにおくのか、で既存事業者とのせめぎ合いがありそうな感じです。

    システム構成

    しかしながら、このシステムイメージ図を見ると色々と問題点が浮かび上がってきます。

    パッと見た感じ普通のID連携モデルです。サービス事業者(先に述べた公共・準公共・民間)がデジタル庁の認証サーバ(OAuthの認可サーバ)に対してクライアント登録をするということになるはずです。このクライアント登録をする段階で公共・準公共・民間の区分で審査をした上で登録していく、という感じで運用していくことになるはずですね。
    しかし、そうなると2点問題があるように感じます。

    課題はなにか?

    1. 多段フェデレーションへの対応が困難
    • これはID連携モデルだけにとどまらずオンプレミスのActive Directoryフォレストの設計を行う上でも昔から考慮点となっていたところですが、デジタル庁の認可サーバに登録されるクライアントが更にID基盤として実際のサービスとID連携を行なっているケースがありえます。例えば、IDaaSなどのサービスを使っている事業者の場合、デジタル庁にクライアント登録されるのはIDaaSとなり、実際のサービスが登録されるわけではありません。このことにより準公共という触れ込みでデジタル庁に登録されたとしても、その先で民間のサービスとID連携をしてしまっていた、、というケースに対応できなくなります。この辺りはルールで縛りを入れる形になるんだと思いますが
  3. デジタル庁のIdPによる行動把握問題
    • フェデレーションモデルということは利用者(今回の場合はマイナンバーカードを持っている国民)がクライアントとなるサービスを使う都度、デジタル庁のIdPへリダイレクトされることになります。今回のケースではクライアントとなりえる公共・準公共・民間のサービスを認証対象となるユーザが使っていることをデジタル庁のIdPは知ることができる、という状態が発生します。まさにGoogle Knows You Better Than You Know Yourselfならぬ「デジタル庁はあなたよりあなた自身のことを知っている」なんてことになるのでは?という疑念を抱かせないように丁寧な説明が必須になると思います。


    この辺りはパブコメだしますかね・・・
    こういうユースケースこそVCを使ったIssuer(デジ庁IdP)→Wallet(個人のスマホ)→Verifier(民間を含むサービス)の3パーティモデルを使ってIssuerとVerifierを分離することが有効なのかもしれません。

    いずれにしても4月に出てくるということなので楽しみにしておきましょう。

    JSONbin.ioを使ってユーザデータベースを作ってみる

    $
    0
    0

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

    ちょっと前にXで局所的に話題になっていたJSONに特化したストレージサービス「JSONBin.io」が気になったので触ってみています。


    何ができるサービスなのか?

    トップページにも記載があるとおり、ざっくりいうと「JSONデータをクラウドに保存してREST APIで操作できるようにしたシンプルなストレージサービス」というところです。

    JSONBin.io provides a simple REST interface to store & retrieve your JSON data from the cloud. It helps developers focus more on app development by taking care of their Database Infrastructure.
    APIを見ると、以下のようなことができるようです。
    • JSONデータ(bin)の管理(作成・更新・読み取り・削除)
      • このサービスではbinという単位でJSONデータを読んでいます
    • コレクションの管理(作成・更新・読み取り)
      • なぜか削除がないです
      • コレクションの配下にbinを入れることでカテゴライズして管理することができます
      • また、スキーマ定義を紐づけることでbinに入れるデータのValidationもできます
    • スキーマの管理(作成・更新・読み取り・削除)
      • コレクションに紐づけるスキーマ定義です。
      • binを作成・更新する際にValidationをするために利用します

    利用プラン

    結構おもしろい考え方で運営されています。現状FreeとProの2つしかプランはありませんが、大きな違いはbinのバージョン管理やスキーマ定義の利用がProしか使えない、という以外はAPIコール数や容量が違うように見えます。
    ここまでだと普通のクラウドサービスっぽいなぁ、と思いますがリクエスト数の考え方と課金の単位が結構面白いです。
    実はFreeプランのリクエスト数に「10,000」とあるのは「Freeプランは上限(月額ではなく)として10,000リクエストまで実行可能ですよ」という意味です。つまり10,000リクエストを超えてこのサービスを利用しようとするとPro($20)にアップグレードするか、Additional Request($15)を購入する必要があります。
    このProもAdditional Requestも月額ではなくリクエスト数を使い果たすまでは払いきりで使えますよ、という課金形態です。

    ですので、例えばスキーマ定義は必要ないけど10,000を超えたリクエストを処理したい、という場合はFree + Additional Requests(500,000リクエスト)を購入となるので$15、スキーマ定義を使いたいが500,000リクエストはいらない、という人はFree + Pro(100,000リクエストまで) を購入するので$20、という形になります。
    ※どちらの場合は一旦はFreeプランについてくる10,000リクエストに加えて各プランのリクエスト数を追加することになりますので、例えばProを契約すると110,000リクエスト使えることになります。

    OpenID Connect coreの標準クレームをサポートするユーザDBを作る

    標準クレームをサポートする、となるとスキーマ定義をしてValidationをかけたくなるのでProを契約する必要があります。支払いはカードやPayPalでできます。私はPayPalを使いました。

    以下の順番で定義をしていきます。
    1. スキーマ定義
    2. コレクションの作成とスキーマ定義の紐付け
    3. binの作成(実際のユーザデータ)
    まずはスキーマ定義です。
    OpenID Connectの標準スキーマはこちらに定義されているのでこれをベースに定義ファイルを作成します。
    こんな感じのデータを作りました。
    {
    "description": "OpenID Connect core 1.0 standard claims",
    "type": "object",
    "properties": {
    "sub": {
    "description": "Subject - Identifier for the End-User at the Issuer.",
    "type": "string"
    },
    "name": {
    "description": "End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.",
    "type": "string"
    },
    "given_name": {
    "description": "Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.",
    "type": "string"
    },
    "family_name": {
    "description": "Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.",
    "type": "string"
    },
    "middle_name": {
    "description": "Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.",
    "type": "string"
    },
    "nickname": {
    "description": "Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael.",
    "type": "string"
    },
    "preferred_username": {
    "description": "Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.",
    "type": "string"
    },
    "profile": {
    "description": "URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User.",
    "type": "string"
    },
    "picture": {
    "description": "URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User.",
    "type": "string"
    },
    "website": {
    "description": "URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User or an organization that the End-User is affiliated with.",
    "type": "string"
    },
    "email": {
    "description": "End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.",
    "type": "string"
    },
    "email_verified": {
    "description": "True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.",
    "type": "boolean"
    },
    "gender": {
    "description": "End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable.",
    "type": "string"
    },
    "birthdate": {
    "description": "End-User's birthday, represented as an ISO 8601-1 [ISO8601-1] YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. Note that depending on the underlying platform's date related function, providing just year can result in varying month and day, so the implementers need to take this factor into account to correctly process the dates.",
    "type": "string"
    },
    "zoneinfo": {
    "description": "String from IANA Time Zone Database [IANA.time-zones] representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles.",
    "type": "string"
    },
    "locale": {
    "description": "End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639 Alpha-2 [ISO639] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.",
    "type": "string"
    },
    "phone_number": {
    "description": "End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension, it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.",
    "type": "string"
    },
    "phone_number_verified": {
    "description": "True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.",
    "type": "boolean"
    },
    "address": {
    "description": "End-User's preferred postal address. The value of the address member is a JSON [RFC8259] structure containing some or all of the members defined in Section 5.1.1.",
    "type": "object"
    },
    "updated_at": {
    "description": "Time the End-User's information was last updated. Its value is a JSON number representing the number of seconds from 1970-01-01T00:00:00Z as measured in UTC until the date/time.",
    "type": "number"
    }
    },
    "required": ["sub"]
    }

    API経由でスキーマ定義(Schema Doc)を作成しても良いですし、管理ポータルから作成することもできます。


    次はコレクションの作成です。
    作成する際にスキーマの関連付けができるので上記で作成したスキーマ定義を紐づけておきます。

    これで準備は完了です。
    では実際のユーザデータをbinとして作成します。
    気をつけるべき点としてはカテゴリとして先ほど作成したコレクションを指定することくらいです。あとはbinに名前をつけておくと一覧を見るときに便利なのでユーザのpreferred_usernameの値などをNameに指定しておくと良いです。(これは理由があるので今後解説します)


    これで利用者の情報がJSONデータとしてサービスに登録できました。
    BIN IDが生成されるので、Postmanなどで実際にAPI経由で情報を参照してみます。
    なお、認証のためヘッダにX-Master-Key(もしくはX-Access-Key)をつけてダッシュボードから確認できるキーの値をセットする必要があります。


    OpenID Providerを作る上で簡易的なユーザDBとしては結構便利な気がしてきましたので、次回以降で組み込んでみたいと思います。




    Appleがアプリ規約を変えたのでApple IDに変わるIdPを考えてみる

    $
    0
    0

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

    AppleがApp Storeの規約を変更したニュースが出ていますね。

    https://gigazine.net/news/20240129-apple-sign-in-with-apple-remove/

    元々はAppleがApp Storeの審査を行う上でソーシャルログインなど複数のIdentity Providerによるログイン機能を有するアプリにはApple IDでのログイン”も”サポートすることを要求していたのですが、今回の規約の改訂で以下の条件を満たすIdPをサポートすれば必ずしもApple IDとのID連携は行わなくても良い、という形に変更されました。

    とはいえ、上記の記事にもある通り、以下の条件を満たすことのできるIdPってApple IDくらいしかないのでは?というなかなか厳しい状態です。

    • the login service limits data collection to the user’s name and email address
    • the login service allows users to keep their email address private as part of setting up their account
    • the login service does not track users as they interact with your app


    ということは、APIでリレーエントリーを作成することができるリレーサービスがあれば実現できるのでは?と思い少し調べてみました。

    触ってみたのはImprovmx(https://improvmx.com/ )というサービスです。※単純に検索して引っかかったので触ってみただけです。

    APIドキュメントを見ているとどうやらAPI経由でエイリアスとなるメールアドレスから実際のメールアドレスへのリレーの定義ができるようです。

    ということで触ってみます。

    まずはベースとなるドメインの定義をします。

    MXとTXTレコードの設定が求められるので、使うドメインのDNSサーバにレコードを作成し、ImprovMXのダッシュボードで確認を行います。

    またAPIを実行するのでAPIキーの発行をしておきます。

    API実行時はBasic認証でユーザ名に「api」、パスワードにAPIキーを設定してあげるだけです。

    やりたいことはユーザがサインアップするときにランダムの仮名メールアドレスを発行し、実メールアドレスを隠した状態でアプリ側へ提供すること、アプリ側から仮名メールアドレスへのメールが送信されたときに実メールに対してリレーされることです。

    そのためにAPIを使って仮名メールアドレスと実メールアドレスを紐づけるAliasを作成することができればOKです。

    この辺りのAPIをつけば良さそうです。

    https://improvmx.com/api/#alias-add

    BodyにJSONでAliasとForwardを指定してPOSTするだけですね。


    GET APIで設定状態の確認をすることができます。


    無料版だと配送までに少し時間がかかりますが、「設定したAlias@設定したドメイン」に対してメールを送るとForwardに指定した実メールアドレスにメールが届きます。

    このAPIコールをIdentity Providerの内部処理として組み込めばAppleが求めるIdPが実装できそうですね。

    旧mac.comのApple IDもトラブルが出ているようですし、特定のIdPだけに頼らずにシステムを作っていけるようにしていきましょう。

    OpenID Providerを作る)ユーザ情報をデータベースから取得する

    $
    0
    0

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

    まだまだ実装すべき点はたくさんありますが、そろそろユーザを固定で埋め込むのではなくデータベースに保存されたユーザ情報を元にIDトークンなどを生成していきたいと思います。

    ただデータベースと言っても、前回紹介したJSONBin.ioを使います。


    その前にこれまでのおさらいです。


    実装する内容

    ユーザ情報を取得する、と言ってもまだユーザ認証画面などを作るところまでは手を出しませんので、これまで固定で埋め込んでいたユーザ情報を止めるところからです。

    とりあえずはユーザのログインID(preferred_username)を指定するとJSONBinからユーザ情報を取得してくる仕組みを作り、ハードコード部分を少しだけ減らしていきたいと思います。

    今回使うJSONBinのAPIは以下の2つです。

      • コレクションに入っているBinの一覧を取得するAPI
      • 最初の10個のBinを取得してくるので、本来は必要に応じてページングをしなければなりませんが、今回は10ユーザも作らないのでページングの考慮はしません
      • APIの仕様としては「https://api.jsonbin.io/v3/c/{コレクションID]/bins」をGETするだけです
      • 結果、Binの一覧がこんな感じで返却されますので、この中でsnippetMeta.nameがpreferred_usernameと一致している要素のrecordの値を持つbinの中にお目当てのユーザの情報が入っている、という仕掛けです。※このsnippetMeta.nameにユーザ名を入れるためにbinを作る際のname指定をしていたわけです

      [
      {
      "private":true,
      "snippetMeta":{
      "name":"test2@example.jp"
      },
      "record":"65b4dfcfdc746540189c4daf",
      "createdAt":"2024-01-27T10:49:51.572Z"
      },
      {
      "private":true,
      "snippetMeta":{
      "name":"test@example.jp"
      },
      "record":"65b4dacd266cfc3fde81ca50",
      "createdAt":"2024-01-27T10:28:29.692Z"
      }
      ]
      • Read a Bin
        • 単体のBinの中身を読み取るAPI
        • 仕様としては「https://api.jsonbin.io/v3/b/{BinのID}」をGETするだけです
        • 結果、指定したBinの中身がこんな感じで返却されてきます
      {
      "record":{
      "sub":"test",
      "name":"taro test",
      "given_name":"taro",
      "family_name":"test",
      "middle_name":"",
      "nickname":"",
      "preferred_username":"test@example.jp",
      "profile":"https://twitter.com/phr_eidentity",
      "picture":"https://1.gravatar.com/avatar/25eee85430bd0bbdcb9cff75655afa43cc9f69bc8730aec852d8538179646ef1",
      "website":"hhtps://idmlab.eidentity.jp",
      "gender":"male",
      "birthdate":"1900-01-01",
      "zoneinfo":"Japan",
      "locale":"jp_JP",
      "updated_at":1704034800,
      "email":"test@example.jp",
      "email_verified":true,
      "address":{
      "formatted":"Kokyogaien, Chiyoda-ku, Tokyo 1000002 JAPAN",
      "street_address":"Kokyogaien",
      "locality":"Chiyoda-ku",
      "region":"Tokyo",
      "postal_code":"1000002",
      "country":"JP"
      },
      "phone_number":"+81-3-1234-5678",
      "phone_number_verified":true
      },
      "metadata":{
      "id":"65b4dacd266cfc3fde81ca50",
      "private":true,
      "createdAt":"2024-01-27T10:28:29.692Z",
      "collectionId":"65b474351f5677401f2691de",
      "name":"test@example.jp"
      }
      }

      これを上手く組み合わせて実装していきましょう。

      ユーザ情報を取得する関数を定義する

      utils/user.jsを前回までの実装でも用意していましたが、ここに一つ新しい関数を追加してみます。まず必要な引数はユーザ名です。これは最終的には利用者が画面で入力したユーザIDを利用することなりますが今回は呼び出し側でログインユーザ名だけはハードコードします。
      また、これは前回までのコードにも書いていますがIDトークン等にどこまで情報を載せるか、についてスコープを使って制御するため、もう一つの引数はスコープとなります。

      こんな関数になります。
      exports.getUserIdentityByLoginId=asyncfunction(login_id, scopes) {

      まずは、JSONBinを実行するための準備です。
      今回はX-Master-Keyにマスターキーをセットします。環境変数などへ仕込んでおくことができます。ちなみにJSONBinではマスターキーとアクセスキーの2種類のキーを発行・管理しています。マスターキーは名前の通りなんでもできるマスターキーなので本来は用途によって権限を絞り込むことができるアクセスキーを使うべきなのかもしれません。
      // JSONBin用のヘッダ
      constheaders=newHeaders({
      "X-Master-Key":process.env.JSONBIN_MASTER_KEY,
      "Content-Type":"application/json"
      });

      いよいよJSONBinのFetch Binsを使ってbinの一覧を取得していきます。
      // JSONBinのユーザCollectionからユーザbinのidを取得する
      constcollectionUrl=newURL(`${process.env.JSONBIN_BASEURL}/c/${process.env.JSONBIN_USERCOLLECTION_ID}/bins`);
      constcollectionResponse=awaitfetch(collectionUrl, {
      headers:headers
      });
      constuserCollection=awaitcollectionResponse.json();

      先ほどのsnippetMeta.nameが関数の引数に指定したlogin_idと一致しているものを抽出します。該当がなければエラーなのでコンソールにメッセージを出しておきます。この辺りはエラーハンドリングやページングの考慮もそのうち必要になりますが今回はスキップしておきます。
      constuserBin=userCollection.find(i=>i.snippetMeta.name===login_id);
      if(typeofuserBin==="undefined"){
      console.log("user not found");
      }else{

      ここまでで取得できた当該ユーザのBinのID(record)をベースに実際のBinの中身を取得し、userIdentityというオブジェクトにセットしておきます。一応ここまでで前回までハードコードしていたユーザの属性情報をJSONBinから取得できた状態になりました。
      // 当該ユーザのBin idからBinの中身を読み出す
      constuserBinUrl=newURL(`${process.env.JSONBIN_BASEURL}/b/${userBin.record}`);
      constuserBinResponse=awaitfetch(userBinUrl, {
      headers:headers
      });
      constuserJson=awaituserBinResponse.json();
      constuserIdentity=userJson.record;

      なお、PPIDへの対応をするためにlocal_identifierをユーザのオブジェクトに指定しておきたいので、subの値を一旦local_identifierの値に待避しておきます。
      // subをlocal_identifierへセット
      userIdentity.local_identifier=userIdentity.sub;

      あとは、前回のスコープに応じた処理を行うという意味で全く同じコードとなります。
      // スコープによって返却する属性の絞り込み
      if(!scopes.includes("profile")){
      deleteuserIdentity.name;
      deleteuserIdentity.given_name;
      deleteuserIdentity.family_name;
      deleteuserIdentity.middle_name;


      ユーザ情報を取得する関数を呼び出す

      もともと認可エンドポイントでユーザ情報を固定で呼び出す処理を書いていたので当該部分を書き換えます。
      oauth2/oauth2.js
      // scopeに応じたユーザの情報を取得する
      // let payload = userIdentity.getUserIdentity(scopes);
      // ユーザ名を指定して属性情報を取得する
      letpayload=awaituserIdentity.getUserIdentityByLoginId("test@example.jp", scopes);

      こんな感じです。元のユーザ情報をハードコードしていた関数をコールする部分をコメントアウトして、今回新しく作った関数にログインIDを指定して呼び出すように変更しています。

      これで完了です。

      JSONBinに入ったユーザ情報がちゃんと取れました。

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

      Entra Verified ID+Microsoft Authenticatorで顔マッチングを行う

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

      そういえば以前のIgnite?Build?でEntra Verified ID+Microsoft Authenticatorの組み合わせのパターンを使うとVerifiable Credentialsを提示する際に、VC内に埋め込んだ顔写真と自撮りのマッチングをすることでVCの持ち主の検証もできるよ、というデモが公開されていたんですが、自前でIssuer/Verifierを実装する場合のサンプルコードが公開されています。(しばらく前に公開されていましたが見る時間が無かったのですがようやくコードを読めました。実際に動かしてはいないです。デモサイトでは試したのですが)

      今回見ていくコードはこちらの中のnode.js版です。

      VCの定義を見ると、こんな感じで写真属性を定義しています。
      - Displayファイル
      {
      "claim": "vc.credentialSubject.photo",
      "label": "User picture",
      "type": "image/jpg;base64url"
      }
      - Rulesファイル
      {
      "outputClaim": "photo",
      "required": false,
      "inputClaim": "photo",
      "indexed": false
      }
      これらの定義を入れたVCを発行するようにIssuerを設定すればIssuer側は完了です。

      実際の顔マッチングはVerifier側の処理となりますので、verifier.jsを見るとこんな感じでfaceCheckのフラグの状態を見て、presentationConfigに写真Claimの名称とマッチングのスレッショルド(マッチ度合いでOK/NGを返す値)を指定しています。
      if ( req.query.faceCheck&&req.query.faceCheck=="1"
      &&!presentationConfig.requestedCredentials[0].configuration.validation.faceCheck ) {
      varphotoClaim=mainApp.config["sourcePhotoClaimName"] ||photo;
      varconfidenceThreshold=parseInt(mainApp.config["matchConfidenceThreshold"]) ||70;
      presentationConfig.requestedCredentials[0].configuration.validation.faceCheck= {
      sourcePhotoClaimName:photoClaim,
      matchConfidenceThreshold:confidenceThreshold
      };
      }

      あとは基本的に従来通りcreatePresentationRequestのエンドポイントにアクセスすれば良いのですが、まだ顔マッチングがベータの機能ということもあり、APIバージョンをbetaにしないといけません。
      if ( payload.includes("faceCheck")) {
      client_api_request_endpoint=client_api_request_endpoint.replace("/v1.0/", "/beta/");
      }

      あとは細かい部分で何箇所か変更する部分もありますが、基本はこの部分が顔マッチング機能を使う場合にVerifier側で対応すべきところです。

      これで単純なVC提示に加えて顔マッチングを組み込むことができそうですね。

      OpenID Providerを作る)login_hintを使ってログインユーザを指定する

      $
      0
      0

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

      徐々にユーザ認証の実装に向けてすすめていきます。

      が、ログイン画面を作るのが面倒くさいのでlogin_hint属性を使ってログイン対象となるユーザを指定するところからスタートしてみます。(完全に暫定対応です)


      その前にこれまでのおさらいです。


      login_hintとは

      本来のlogin_hintを含むhint系の属性の使い方はOpenID Providerの前段で何らかの処理を行った結果をOpenID Providerに伝えるために使われるパラメータです。
      例えば、id_token_hintは以前RPに対して発行したid_tokenを引き継いで追加で認証や属性の送出を行う場合など(典型的な例だと、Entra IDにおける多要素認証の強制)に用いられます。
      今回のlogin_hintは仕様を見るとこのように記載されています。
      OPTIONAL. Authorization Server に対する End-User ログイン識別子のヒントとして利用される. RP が End-User に Email アドレス (もしくはその他の識別子) を要求し, それを Authorization Server にヒントとして送信することでヒントとする. Discovery に利用された値をヒントの値として利用することを推奨する (RECOMMENDED). この値は phone_number Claim に指定されるフォーマットに従った電話番号でもよい (MAY). このパラメータを利用するか否かは OP の判断にゆだねる.
      要するにRPからOPへログインさせるユーザに関するヒントを提供する、というためのパラメータですね。
      今回はヒントと言いつつこの属性をそのままユーザ識別子として利用してしまいたいと思います。

      login_hintの指定方法

      単純に認証リクエストにクエリパラメータとしてlogin_hint=xxxという形で指定するだけです。
      例)
      localhost:3000/oauth2/authorize?scope=openid address phone&response_type=id_token&client_id=123&redirect_uri=https://jwt.ms&state=hoge&login_hint=test@example.jp
      内部処理も単純にクエリパラメータを取得するだけですし、今回のケースだとそのままユーザストアを検索するためのキーとして利用します。

      login_hintの取得
      // login_hint属性からユーザ情報を取得する
      constlogin_hint=req.query.login_hint;

      ユーザ情報の取得
      // ユーザ名を指定して属性情報を取得する
      // login_hintを使う
      letpayload=awaituserIdentity.getUserIdentityByLoginId(login_hint, scopes);


      これでユーザ情報を取得し、id_tokenを発行できました。


      今回はここまでです。
      そろそろ真面目に認証画面を作ろうかと思いつつ面倒臭さが勝っている今日この頃です。

      OpenID Providerを作る)仮名に加えて匿名をサポートする

      $
      0
      0

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

      厳密にはOpenID Connectの仕様に定義されていませんが、以前実装したPairwise識別子(PPID)に加えて匿名の識別子(Transient Identifier)をサポートしたくなるシーンが存在するので、対応を入れていきたいと思います。

      その前にこれまでのおさらいです。


      識別子の種別、および仮名と匿名の違い

      識別子は主体を一意に特定するために利用される属性(群)を表しますが、名寄せのリスクなどに応じてどのような値を返すべきかについて十分な考慮が必要です。

      そもそも識別子には公開、仮名(かめい)、匿名の3種類が存在します。
      区分共有範囲OpenID Connectでの区分SAMLでの区分解説
      公開全体publicunspecifiedやemailaddress全RPに対して共通の識別子を送出する
      仮名RPpairwisepersistent特定のRPに対して一意の識別子を送出する
      匿名なし-transient毎回異なる識別子を送出する

      上記の表に示した通り、全てのRPに対して同じ識別子の値を送出するpublicなタイプ、同一のRPに対しては毎回同じ値を送出するPairwiseがOpenID Connectにおいては定義されています。ただし、特定のケースにおいて同一識別子出会ったとしても毎回送出する識別子を別のものに変えたい、というケースも存在します。SAMLにおいてはこのケースを想定してnameid-formatとしてpersistent(いわゆる仮名であるPairwise)に加えてtransient(匿名)を定義していました。
      ここで仮名と匿名の違いを正しく理解しておきましょう。
      • 仮名:連続性がある。つまり外野から見ると誰だか分からないが、特定のRPから見ると一意に主体を識別できる
      • 匿名:連続性がない。外野から見ても特定のRPから見ても一意に主体を識別することができない

      この辺りはかなり昔にイベントでしゃべった資料が出てきたので貼り付けておきます。


      実装してみる

      前置きが長くなりましたが、実装してみましょう。
      基本的にはpairwiseの中の種別として実装をしてみたいと思いますので、設定ファイルにスイッチを作っておき、指定されたオプションによって仮名か匿名かを決めていきたいと思います。

      .envに識別子のタイプを指定できるようにします。
      # 識別子の種別:PERSISTENT or TRANSIENT
      IDENTIFIER_TYPE=TRANSIENT

      次に識別子を生成するところのロジックを分岐させます。
      oauth2/oauth.js
      // 識別子タイプの判定(Persistent or Transient)
      letPPID;
      switch(process.env.IDENTIFIER_TYPE){
      case"TRANSIENT":
      // 魔界異なる識別子を生成する
      PPID=crypto.randomUUID();
      break;
      case"PERSISTENT":
      default:
      // Pairwise識別子の生成
      PPID=utils.createPPID(payload.local_identifier, req.query.redirect_uri);
      }

      以上で完成です。

      初回アクセスでの識別子(sub)の値

      2回目のアクセス時の識別子(sub)の値


      同じRPへのアクセスですがsubの値が変わっていることがわかります。

      今回のコードを含めこちらに挙げてありますので参考にしてみてください。



      OpenID Providerを作る)ユーザ認証方式について考える

      $
      0
      0

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

      いよいよユーザの認証について実装を考え始めているのですが、最大の考慮ポイントはパスワードを使ったユーザの認証を実装するかどうか?です。

      その前にこれまでのおさらいです。


      問題はパスワード認証を許容するかどうか

      モダンな認証基盤を作ろうと思うとパスワードを使った知識認証は極力省きたくなります。しかしながら実際に認証基盤を導入していくと必ず出てくるのが認証画面はアプリ側で実装したい、OpenID Providerへのリダイレクトはさせたくない、という要件です。

      もちろんこれは集中的にOpenID Providerでアイデンティティを守る、特にログイン画面をフィッシングから守るという主旨と相入れないものなのですが、一貫したUXを守りたいというアプリ側の意見も一定理解できます。

      そのため、OpenID Providerの中にはネイティブアプリからバックエンドで呼び出す認証エンドポイントを独自に実装したりしているケースも存在しますし、安直にOAuth2.0のリソースオーナーパスワードグラントを利用しよう、という話になってしまうケースも存在します。

      なお、リソースオーナーパスワードグラントについては明確に注意点が記載されているので、どうしても使わないとダメなケース(主にはシステム移行)以外では採用すべきではありません。

      リソースオーナーパスワードクレデンシャルグラントタイプは, リソースオーナーがクライアントと信頼関係にある場合, 例えばリソースオーナーの所有するデバイスOSや特別許可されたアプリケーションなどに適している. 認可サーバーはこのグラントタイプを有効にする際は特に注意するべきである. また他のフローが利用できない場合にのみ許可するべきである.

      このグラントタイプは, リソースオーナーのクレデンシャル (通常は対話型入力フォームにて取得するユーザー名とパスワード) を取得できるクライアントに適している. また, 保存済みのクレデンシャルをアクセストークンへ変換できるため, Basic認証やDigest認証のような直接的な認証スキームを利用している既存のクライアントをOAuthへ移行する際にも利用できる.


      こう考えると新規でOpenID Providerを作る場合はTokenエンドポイントがサポートするgrant_typeからpasswordは落としてしまっても良さそうではあります。


      その場合の考慮点

      しかし、安直にパスワード認証をなくしてしまうと何が起きるのか?も考慮しておかないといけません。

      先のユースケースのようにネイティブアプリケーションへのバックエンドAPI経由でのインテグレーションをしたいケースへの対応、そしてパスワード以外の認証手段のみしか提供しなかった場合のアカウントリカバリの方式をどうするか?などセットで考えなければならないことが出てきます。

      ネイティブアプリへの認証APIの提供

      • ここは個人的な意見としては落としてしまっても良いかとは思いますが、アプリケーションチームとの力関係もあると思うので十分な話し合いや安全性に関する検証が必要な領域です。

      リカバリ方法

      • 安全なアカウントリカバリ方法が提供できない場合は、いくらデフォルトの認証手段を強固にしたとしても、そこから突破されるので意味がありません。そのため認証手段を強くする際はリカバリ手段もしっかりと固める必要があります。(最近某C2C取引アプリでdiscoverable credentialが実装される前のWebAuthnが実装された状態からパスキーへ移行したことにより、過去にデバイスにバウンドされた鍵しか登録しなかったユーザがログインできなくなるという事件もありましたので、場合によっては過去の経緯やユーザの状態を含め分析をしないといけません)


      今回はせっかく新規で作るのでやっぱりパスキーをデフォルトにしてしまうのが良いのかなぁ、、と思案中です。(ここまでくるとOpenID Connectの仕様のスコープ外なのですが)



      タイのデジタルIDについて現地の声を聞いてみた

      $
      0
      0

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

      といってもそれほど大袈裟な話ではありません。(単なる旅行記です)


      以前、局所的に注目を集めていたタイのETDA(デジタル電子取引開発庁)が発行しているデジタルアイデンティティガイドラインについてです。中身については森さんが詳しく調べて記事をあげていらっしゃいますのであまり触れません。(素晴らしい記事です)


      完全に個人旅行でタイを訪れたわけですが、以前より縁のある某現地金融機関のディレクターの方と現地でお会いする機会があり、デジタルアイデンティティ(主にVerifiable CredetialsとEUのガイドライン)に関する議論をする機会を得たので、その中でETDAのガイドラインがシンプルで素晴らしいって話題になってるよ、という話を振ってみたわけなんですが、「EUのガイドラインのコピーなんじゃないの、くらいでとらえてるよ〜HAHAHA」なんて言われてしまいました、というオチです。どっちかというとNISTなのかな?と思っていたんですけどね。

      しかしこういうガイドラインは縁の下の力持ちではあるものの、世界へ発信していくことが国際競争力の源泉になるので日本もそうですがちゃんとグローバルに情報発信していくことが大事ですよね。


      象です。


      ストリートファイターIIのサガットの背景で有名な涅槃仏です。


      トムヤムクンです。現地で飲むシンハービールとの組み合わせは最高でした。




      アイデンティティ関連の重要イベントまとめ(2024年版)

      $
      0
      0

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

      多くの日本企業はそろそろ2023年度も終盤に差し掛かり、新年度の予算の計上の大詰めを迎えているのではないでしょうか?

      そんな時だからこそ、年次で開催されるデジタル・アイデンティティに関連する重要なイベントに参加する予算を捻じ込んで計上しておくべきです。

      ということで私的な観測ですがイベント一覧をまとめていきます。(みなさんから追加でこれも!というご意見があれば適宜アップデートしていきます)

      なお、開催場所・開催時期については開催年によって異なることがありますので、一旦は2024年版を記載していきます。(順序に意図はありません)


      Internet Identity Workshop(IIW) #38, #39

      • 期間:
        • #38:2024年4月16日〜18日
        • #39:2024年10月29日〜31日
      • 場所:サンフランシスコ(マウンテンビュー)
      • URL:https://internetidentityworkshop.com/
      • 概要・特色:
        • 言わずと知れたIIWです。毎年2回、春と秋に開催されます。アンカンファレンス形式なので英語でディスカッションに参加することが前提となり、若干参加のハードルは高めですがまだ世の中に製品やサービスとして実装される前の次の技術についての議論が行われるのでとても勉強になります。また製品やサービスを実装する側の立場の方なら「こんな悩みがあるのでどうしたらいいと思う?」という投げかけを専門家コミュニティに対して行う良い機会ともなります。
      • オススメの参加方法:
        • 定点観測がとても重要なイベントなのでなるべく毎回出ると良いと思います。
        • 主に数年先の技術がどうなるかを観測するには大変便利な機会です。
      • その他:
        • イベントの前後でOpenID Foundation WorkshopやWGのIn person meetingなどの関連イベントやワークショップが開催されますのでこちらも参加すると良いと思います。(ちなみにこれはIIW以外のイベントでも共通です)
        • マウンテンビューの田舎でやるのでグルメ的な楽しみは前後にサンフランシスコのダウンタウン周辺に一泊する方が良いと思います。

      European Identity and Cloud Conference(EIC)

      • 期間:2024年6月4日〜7日
      • 場所:ベルリン
      • URL:https://www.kuppingercole.com/events/eic2024
      • 概要・特色:
        • こちらも言わずと知れたEICです。KuppingerCole主催ということで毎年ドイツで開催されます。しばらく前まではミュンヘン郊外で開催されていましたが、ここ数年はベルリンに場所を移しています。
        • こちらはIIWに比べると展示会の色が強いので、数多くのベンダが自社製品の展示をしたりセッションをもったりしています。
        • また、ヨーロッパ開催ということでEUのデジタルIDに関する取り組みについて聞くこともできるのが最大の特徴かもしれません。
      • オススメの参加方法:
        • こちらももちろん定点観測が重要ですが、今を知る機会にもなりますのでセッションだけでなくベンダのブースを回って情報収集をするのも良いでしょう。
        • これもドイツならではですが夕方になるとビールを出してくれるブースもあります。
      • その他:
        • ミュンヘン開催の頃は数は限られるものの周辺に良いレストランやビアハウスがありました。もちろんベルリンに移ったことで選択肢は増えたのですが個人的にはミュンヘンの素朴な感じのレストランの方が好きでした・・・
        • とはいえ街なので観光スポット(ベルリンの壁とかブランデンブルク門とか)も多くありますし、メタルバーもありますのでジャーマンメタルが好きな人は行かないと地獄に落ちると思います。

      Identiverse

      • 期間:2024年5月28日〜31日
      • 場所:ラスベガス
      • URL:https://identiverse.com/
      • 概要・特色:
        • こちらも言わずと知れたIdentiverseです。2016年(たしか)くらいまではCloud Identity Summitという名称で開催されていました。主催のPing Identityの買収に伴いIdentiverseという名称に変更されました(経緯は不正確かも)が、その後も継続的に開催されています。
        • こちらのイベントは「お祭り」です。もともとPing IdentityのCEOのAndreがお祭り好きということもあるのでしょうが、オープンニングのショーがあったり、パーティや展示会場でのアトラクションなども充実しているので楽しくネットワーキングをするには最適なイベントです。
        • また、Master Classというシリーズでスポンサーの製品やサービスに関するトレーニングなども実施されるため、実際に現場にデプロイする人たちにとっては良い機会になると思います。
      • オススメの参加方法:
        • セッションはもちろん、ネットワーキングにより得られることも非常に多いと思いますので、重鎮と繋がって意見交換をする場として捉えるのも良いかもしれません。
      • その他:
        • ラスベガス開催になってから実は私は参加していないのですが、ラスベガスが好きな人は色々な楽しみがあると思います。(もう10年以上前ですが私がラスベガスでのイベントに参加した時はグランドキャニオンまで足を伸ばしました)

      OAuth Security Workshop(OSW)

      • 期間:2024年4月10日〜12日
      • 場所:ローマ
      • URL:https://oauth.secworkshop.events/
      • 概要・特色:
        • 名前の通り、OAuthやOpenID Connectなどのセキュリティを強化することを目的としたイベントです。
        • 私は実際に参加したことがないのですが、資料などをみていると結構ディープな議論がなされているようです。
        • また、開催場所は毎回異なる(主にヨーロッパ)ので色々な場所を楽しむことができます。
        • 開催時期も結構まちまちな気がします。(去年は8月だったはず)
      • オススメの参加方法:
        • 参加したことがないので実際のところは分かりません。ただどのイベントでもそうなのですが、セッションの横でコアな議論をしている人たちがいるので、そのようなサイドイベントにも参加できると良いと思います。メーリングリストなどに参加しているとそのような議論がいつどこで行われるか発見できると思います。
      • その他:
        • 毎回開催場所が異なるのはいいですね。行きたい場所で開催される時に参加できるとモチベーションも上がると思います。
        • 今回の開催時期は日本企業には厳しいですね・・・

      IETF #119, #120, #121

      • 期間
        • #119:2024年3月16日〜22日
        • #120:2024年7月20日〜26日
        • #121:2024年11月2日〜8日
      • 場所:
        • #119:ブリスベン
        • #120:バンクーバー
        • #121:ダブリン
      • URL:https://www.ietf.org/
      • 概要・特色:
        • IETFなのでもちろんアイデンティティ関係はごく一部ですが、非常に重要なトピックスをディープに議論する場になっていると思います。例えばOAuthとかVerifiable Credentials(特にSD-JWT)など。
        • ただ、私は実際には参加したことがないので詳しくは知りません。前々回の横浜開催の時に参加できれば良かったのですが、3月初旬というこれまた日本企業の管理職には厳しい時期だったので断念した経緯があります。。。
      • オススメの参加方法:
        • 参加したことがないので分かりませんが、ディープな議論ができるはずです。色々な場所で開催されるのもあり、ネットワーキングも進みそうです。
      • その他:
        • OSWとも共通しますが開催場所ごとの楽しみがありそうです。

      W3C TPAC

      • 期間:2024年9月23日〜27日
      • 場所:アナハイム
      • URL:https://www.w3.org/events/tpac/2024/tpac-2024-hybrid-meeting/
      • 概要・特色:
        • こちらもIETFと同様、W3Cなのでアイデンティティ関係はごく一部ですが、Verifiable Credentialsに関する議論は重要なトピックスと言えるでしょう。
        • 私も対面で参加をしたことがないのですが、ハイブリッド開催なのでリモートでも参加できるのは良い点なのかもしれません。ただし議論への参加は結構厳しい印象ですが。
      • オススメの参加方法:
        • 現地参加がやっぱり良いんじゃないかと思います。ディープさが違うはずですし、ネットワーキングができるので。
      • その他:
        • こちらも毎回開催場所が異なるので現地ならではの楽しみ方がありそうです。

      RSA Conference

      • 期間:2024年5月6日〜9日
      • 場所:サンフランシスコ
      • URL:https://www.rsaconference.com/usa
      • 概要・特色:
        • ベンダ系と言いつつ老舗のセキュリティ・カンファレンスになってきています。
        • デジタルアイデンティティ関連もセキュリティと絡めて毎年取り上げられます。
      • オススメの参加方法
        • 最近は私も現地参加していないので様子は分かりませんが、過去に参加した時の感覚ではやはりビジネス色が強いのでセッションにつわえてブースを回ってみると製品やサービスのトレンドを感じることができると思います。
      • その他:
        • サンフランシスコダウンタウン開催なので見て回る先は色々とあります。
        • 最近はダウンタウンの治安は悪いので要注意です。

      他にももっとディープなイベント(Rebooting Web of Trustなど)もありますし、ベンダ主催のイベント(OktaのOktaneなど)や、もちろんOpenIDファウンデーション・ジャパンが開催するMeetup(TechnightやBizDay)もあります。
      全部に参加するのは現実的ではないと思いますが、業界の重鎮は大体上記で紹介したイベントには来ているので参加してネットワーキングをするだけでも今後のアイデンティティ業界での生きやすさが変わってきますので、ぜひ参加して交流をしてみてください。

      Walletアプリを同一端末にインストールする際の注意点

      $
      0
      0

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

      先日のOpenID for Verifiable Credentials関連仕様に関するセキュリティ分析の中でも触れましたが、Issuer/Holder/Verifierの3パーティモデルにではどうしてもクロスデバイス(Issuer/VerifierはPCブラウザ、Holderはスマホアプリ)のユースケースや、同一デバイス(スマホ)においてもブラウザとアプリを切り替えて利用するユースケースを想定せざるを得ません。

      参考)


      そのためにはWalletアプリがIssuance/Presentationに利用するカスタムURLスキームを定義する必要がありますが、例えばiOSの場合は最後にインストールしたアプリが当該のスキームを占有してしまう問題があります。その中でも最大の問題は、利用者から見てどのアプリがどのカスタムURLスキームを利用しているのかを判別することが難しい点にあります。
      具体的に何が起きるか、というと利用者が無意識にインストールするアプリケーションがOpenID for Verifiable Credentialsで利用するカスタムURLスキームを利用していた場合、利用者は正しいVerifiable Credentialsを保有していたとしてもそれをうまく利用できなくなってしまいます。こうなるとIssuerやVerifierのサービスを提供している事業者からすると、利用者から問い合わせがあったとしても何が起きているのかわからず、単にうまくWalletが起動しない、という状況に見えてしまうわけです。

      こんな感じになってしまします。(iOSの場合)
      • クロスデバイスのケース(カメラでQRコードを読み込む場合)
        • 基本的に後からインストールしたアプリが起動してしまいます。
        • 後からインストールしたアプリの実装によってはカメラアプリからの起動に失敗して何も起きない、という見え方をするケースもあります。
      • 同一デバイスのケース(スマホブラウザからWalletアプリを起動する場合)
        • 後からインストールしたアプリを起動しようとしてしまいます。
        • こんな感じです。(本来はMicrosoft Authenticatorを立ち上げようとしているのですが、別アプリが立ちあがろうとしてしまっています)
        • 本来はこのようにAuthenticatorが起動します。



      この状態を解消するには後からインストールしたアプリを削除するしかないので、もし後からインストールしたアプリも利用したい場合やすでに後からインストールしたアプリにもVCを発行してしまった場合などは諸々やり直しが起きてしまいます。

      仕組み上どうしようもありませんが、なんとかなりませんかね。。。Appleさん。

      Entra Verified ID+Microsoft Authenticatorでの顔マッチングのデモ

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

      先日、Entra Verified ID+Microsoft AuthenticatorでVerifiable Credentialsの中の写真情報と自撮りの顔マッチングをすることによりVerifiable Credentialsの検証に加えて提示者の確認を実施することができるようになった、というポストをしました。
      某ネコ氏にデモが見たいというリクエストをもらったのでデモ動画をとってみました。



      と思ったらちょうどPublic Previewの案内が公式に出てますね。

      OKパターン


      最近撮った写真を使ってVCを発行したのでまぁOKです。

      NGパターン



      宣材写真は・・・・・ダメですな。プロの加工の技は顔マッチングを突破できないということですね。


      しかし、この仕組みですがマッチング対象とできる写真が1枚だけ、という段階で実用は結構厳しいんじゃないですかね。一応メガネの有無くらいはちゃんと判別してくれていますが、マッチング対象となる情報をもう少し充実することができれば使い勝手は良くなるんじゃないかと思います。

      Viewing all 769 articles
      Browse latest View live