こんにちわ、mitsuo3といいます。
この記事はDigital Identity技術勉強会 #iddance Advent Calendar 2024の5日目の記事です。
初めてのQiita投稿なので、読みづらいかもしれませんがお付き合いください。
さて早速ですが「IDチームの責任範囲」について私のモヤモヤを言語化してみようと思います。
同じような悩みを抱えている方いらっしゃればぜひお話させて頂きたいので、ご一読頂けますと幸いです。
OIDCにおけるOPとRPの役割
OpenID Connect(OIDC)の規格を読み、理解を深めている中で疑問が湧きました。
OIDCでは、OP(OpenID Provider) と RP(Relying Party) の双方が仕様を守ることが前提となっています。
しかしOPとRPは求められる役割が異なるので、システムの作り手は別のチームになることが多いと思います。
偏った理解かもしれませんが、id_token
周辺でのOP/RPは以下の役割だと理解しています。
OP(OpenID Provider)
- OPは名の通りOpenID提供者である
- ユーザーの認証を担保する役割を持つ
- 認証情報(
id_token
など)を正確に生成し、RPに提供する責任がある- 例: ユーザーが入力した情報を基に認証を行い、
nonce
やsub
を含む署名済みのid_token
をRPに返す
- 例: ユーザーが入力した情報を基に認証を行い、
RP(Relying Party)
- RPはBtoB(or BtoC)のサービス提供者である(認証や認可はサービスを利用する際の前提条件)
- OPから提供された認証結果を基に、サービス設計やセキュリティチェックを行う
- OPから返却された
id_token
を検証し、セキュリティ上必要な項目(nonce
など)の確認を行う- 例:
id_token
内のnonce
が送信時と一致するかを確認することで、リプレイ攻撃を防ぐ
- 例:
立場の異なる両者が正しく実装しなければOIDC規格で定められている仕様を満たさないことになるため、セキュリティリスクが生じてしまいます。
具体例
nonce
の取り扱いを例に取り上げます。
-
nonce
とは?:-
nonce
はRPが生成し、OPに送信する一意の値です - これにより、リプレイ攻撃や認証トークンの偽造を防ぎます
-
-
仕様におけるMUST要求:
- OPは、
id_token
内にnonce
を含めて返却しなければなりません - RPは、受け取った
id_token
内のnonce
が自分で送信したものと一致しているかを確認しなければなりません
- OPは、
図の④番はRPのみの実装であるため、OP側はRPで実装されていないことに気付くことはできません。
nonce
の検証を実施していない場合、リプレイ攻撃を受けるリスクが残存してしまいます。
id_tokenの検証に必要な項目
他にもRP側で実装すべき内容(OP側が関与しない内容)が多数存在します。
3.1.3.7. ID Token Validation(日本語訳文章の転載なので折り畳みます)
- Client は Token Response 内の ID Token を確認しなければならない (MUST).
- ID Token が 暗号化されているならば, Client が Registration にて指定し OP が ID Token の暗号化に利用した鍵とアルゴリズムを用いて復号する. Registration 時に OP と暗号化が取り決められても ID Token が暗号化されていなかったときは, RP はそれを拒絶するべき (SHOULD).
- (一般的に Discovery を通して取得される) OpenID Provider の Issuer Identifier は iss (issuer) Claim の値と正確に一致しなければならない (MUST).
- Client は aud (audience) Claim が iss (issuer) Claim で示される Issuer にて登録された, 自身の client_id をオーディエンスとして含むことを確認しなければならない (MUST). aud (audience) Claim は複数要素の配列を含んでも良い (MAY). ID Token が Client を有効なオーディエンスとして記載しない, もしくは Client から信用されていない追加のオーディエンスを含むならば, そのID Token は拒絶されなければならない.
- ID Token が複数のオーディエンスを含むならば, Client は azp Claim があることを確認すべき (SHOULD).
- azp (authorized party) Claim があるならば, Client は Claim の値が自身の client_id であることを確認すべき (SHOULD).
- (このフローの中で) ID Token を Client と Token Endpoint の間の直接通信により受け取ったならば, トークンの署名確認の代わりに TLS Server の確認を issuer の確認のために利用してもよい (MAY). Client は JWS [JWS] に従い, JWT alg Header Parameter を用いて全ての ID Token の署名を確認しなければならない (MUST). Client は Issuer から提供された鍵を利用しなければならない (MUST).
- alg の値はデフォルトの RS256 もしくは Registration にて Client により id_token_signed_response_alg パラメータとして送られたアルゴリズムであるべき (SHOULD).
- JWT alg Header Parameter が HS256, HS384 および HS512 のような MAC ベースのアルゴリズムを利用するならば, aud (audience) Claim に含まれる client_id に対応する client_secret の UTF-8 表現バイト列が署名の確認に用いられる. MAC ベースのアルゴリズムについて, aud が複数の値を持つとき, もしくは aud の値と異なる azp の値があるときの振る舞いは規定されない.
- 現在時刻は exp Claim の時刻表現より前でなければならない (MUST).
- iat Claim は現在時刻からはるか昔に発行されたトークンを拒絶するために利用でき, 攻撃を防ぐために nonce が保存される必要がある期間を制限する. 許容できる範囲は Client の仕様である.
- nonce の値が Authentication Request にて送られたならば, nonce Claim が存在し, その値が Authentication Request にて送られたものと一致することを確認するためにチェックされなければならない (MUST). Client は nonce の値を リプレイアタックのためにチェックすべき (SHOULD). リプレイアタックを検知する正確な方法は Client の仕様である.
- acr Claim が 要求されたならば, Client は主張された Claim の値が適切かどうかをチェックすべきである (SHOULD). acr Claim の値と意味はこの仕様の対象外である.
- auth_time Claim が要求されたならば, この Claim のための特定のリクエストもしくは max_age パラメータを用いて Client は auth_time Claim の値をチェックし, もし最新のユーザー認証からあまりに長い時間が経過したと判定されたときは再認証を要求すべきである (SHOULD).
先述したnonce以外にも4つのMUST要件が存在します。
またSHOULDまで含めてすべて実装すると相当数の作りこみが必要になるため、RPが実現したい「その先のサービス」を優先で考える方々には、セキュリティ担保のための開発は優先順位が落ちるイメージです。
OPとRPの関係
ほとんどのケースでは、OP1つに対してRPは複数あることが多いと思います。
またRP側で実装する認証認可に関わる作りは各RPで実装することになりますが、各RPでほとんど変わらない実装をそれぞれで作る必要が出てくるので効率が悪いと感じています。
先ほど問題点で述べたRP側でも正しい実装をしないとセキュリティリスクが残存してしまうため、正しく実装することが必要になるのですが、RPの数だけOIDCの実装に詳しい方がいるとは限りません。
OP側はID技術に長けた人をアサインメントするはずなので、RP側の認証・認可の部分にも関与する方が効率は良いと考えています。
まとめ
私が思うIDチームの責任範囲は、チームトポロジーでいうところのコンプリケイテッド・サブシステムチームの考えに近いです。
OP側にアサインメントされているID技術者がRP側に幅出しすることで、RP側の認知負荷を極力下げてあげることが重要だと考えます
そのためにも私自身、技術・組織運営に磨きをかけていかなくては・・・と自戒の意味での文章でした。
(おわり)
おまけ
人生で初めてWeb記事を執筆したのですが、とても勉強になりました。
※そして何度自分で読み返しても読みづらい文章だなと反省
特に社内向けに共有することと、社外に向けて共有することは雲泥の差(読み手が幅広い)を改めて感じました。
※言語化しようとした際になんとなく理解しているだけなので言語化できないことも多々あり。
書き手側の気持ちを少しだけ知ることができたので、また何かきっかけがあれば書いてみます。