+ 作る
図を読み込んでいます…
1 / 14
図でつながる 認証と暗号の基礎 / 全11章

第11章 CIMD ― client_id を、アプリの自己紹介が置かれた URL にする

発行
かえで
版
第2版
改訂
分量
14コマ・7,333字

OAuth の client_id を、アプリの自己紹介 JSON を置いた HTTPS の URL にする CIMD を図解。取得と照合の流れ、秘密を書かない理由、なりすましと SSRF への備えを草案 -03 に沿って説明し、本全体も振り返ります。

出会ったことのない相手と、つながる

第9章の OAuth では、アプリ(クライアント)は認可サーバーにあらかじめ登録され、そこで発行された client_id という名前で呼ばれていました。第10章の OIDC も、その上に乗った仕組みです。最終章では、その手前にある「アプリは、どうやって認可サーバーに知ってもらうのか」という入口を扱います。

事前登録は、つなぐ相手が決まっているときはうまくいきます。開発者が認可サーバーの管理画面でアプリの名前や戻り先(redirect_uri)を届け出て client_id を受け取り、それをアプリに書き込んでおけばよいからです。図の左上の矢印が、この「登録済み」の関係です。

ところが AI のツールの世界では、この前提が崩れます。たとえば MCP(Model Context Protocol)では、AI アプリが「MCP サーバー」と呼ばれる外部の道具につながり、その道具を使う許可を OAuth でもらいます。利用者が新しい MCP サーバーを足すたびに、アプリは初めて会う認可サーバーと話すことになります。図の B や C のように相手が次々に増えると、一つずつ事前登録をしていては追いつきません。

  • 出会ったことのない相手と、つながる
  • サーバー
  • 認可サーバーA
  • 登録済み
  • ブラウザ
  • サーバー
  • 初めて
  • 認可サーバーB
  • AIアシスタント
  • 初めて
  • サーバー
  • 認可サーバーC
  • 相手が増えるたびに、事前の登録が要る

登録の窓口を開く:動的クライアント登録

事前登録の手間を減らす方法として、OAuth には 動的クライアント登録(RFC 7591)があります。認可サーバーが登録の窓口となる URL を用意し、アプリはそこへ自分の名前や戻り先を書いた JSON を POST します。認可サーバーは新しい client_id を発行して返します。図では、申し込みの矢印が右へ進み、client_id の札が左のアプリに戻ってきます。

人の手を介さずに登録できるのは便利ですが、困りごともあります。窓口はだれでも使えるようにしておくことが多いので、認可サーバーには登録がどんどんたまります(図の右下)。一度きりで使われなくなったアプリの登録を、いつ消してよいかも分かりません。アプリの側も、つながる認可サーバーごとに別々の client_id(ときには秘密の文字列も)をもらい、覚えておく必要があります。

MCP の認可の仕様は、2026-07-28 版で動的クライアント登録を「非推奨(互換のために残すもの)」とし、代わりに次のページから説明する方法を勧めるようになりました。

  • 登録の窓口を開く:動的クライアント登録
  • ブラウザ
  • サーバー
  • 登録の申し込み(POST)
  • client_id を発行
  • AIアシスタント
  • 認可サーバー
  • client_id: 8f3a…
  • 登録 #1
  • データベース
  • 登録 #2
  • 登録 #3 …
  • 登録がたまる
  • 相手ごとに登録が増え、消してよい時期も分からない

client_id を、自己紹介が置かれた URL にする

そこで考えられたのが CIMD(OAuth Client ID Metadata Document)です。発想は単純で、認可サーバーに番号を発行してもらう代わりに、アプリが自分で 自己紹介の JSON を自分のサイトに公開し、その JSON の置き場所である HTTPS の URL を、そのまま client_id として使います。図では、さっきの「8f3a…」という番号が URL に変わり、その先に自己紹介の文書が現れます。

たとえるなら、名刺に会社の URL が刷ってあり、受け取った人がその URL を開いて会社案内を読むようなものです。認可サーバーは事前の届け出がなくても、URL を開けばアプリの名前や戻り先を知ることができます。ただし名刺と同じで、そこに書いてあるのは本人の 自己申告 です。何を信じてよいかは、このあとの照合と注意点で詰めていきます。

CIMD はまだ RFC(確定した標準)ではなく、IETF の OAuth ワーキンググループで検討中の草案です。この章は、2026年11月6日に draft-ietf-oauth-client-id-metadata-document の版 -03(2026年11月2日付)を確認して書いています。今後も細部が変わる可能性があります。

  • client_id を、自己紹介が置かれた URL にする
  • client_id
  • ブラウザ
  • https://assist.example/client.json
  • サーバー
  • この URL に置いてある
  • AIアシスタント
  • 認可サーバー
  • 自己紹介の JSON client_name: AIアシスタント redirect_uris: …/callback
  • client_id そのものが、自己紹介の置き場所を指す

アプリが自己紹介の JSON を公開する

まずアプリの開発者が、自己紹介の文書を用意して自分のサイトに置きます。例として、AI アシスタントのアプリが https://assist.example/client.json に次の JSON を置くとします(.example は説明用に予約されたドメインです)。

json
{
  "client_id": "https://assist.example/client.json",
  "client_name": "AIアシスタント",
  "client_uri": "https://assist.example/",
  "redirect_uris": ["https://assist.example/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"]
}

いちばん大事なのは、中の client_id が、この文書を置いた URL と一字一句同じであることです。client_name は同意画面に出す名前、redirect_uris は第9章で見た「認可コードを受け取る戻り先」の一覧です。クライアント認証の方式を書いていないので、このアプリは秘密を持たない パブリッククライアント(方式 none)として扱われます。版 -03 では、方式を書くときは token_endpoint_auth_methods_supported という一覧の項目を使うよう勧める形に変わりました。

URL にも決まりがあります。https であること、パスがあること(-03 からはルートの / だけも不可)、# 以降のフラグメントや user:pass@ のような利用者情報を含まないこと、. や .. の区切りを含まないことです。クエリ(? 以降)も付けないよう勧められています。文書は 200 の応答で、JSON として返します。

  • アプリが自己紹介の JSON を公開する
  • https://assist.example/client.json
  • client_id … この URL と同じ client_name … 表示名 redirect_uris … 戻り先の一覧
  • ブラウザ
  • サーバー
  • https のみ
  • パスは / 以外
  • # は付けない
  • AIアシスタント
  • 認可サーバー
  • 200 応答の JSON。中の client_id は URL と同じ文字列

認可リクエストに、URL を入れて送る

ここで利用者のアオイが登場します。アオイが AI アシスタントに「この MCP サーバーを使いたい」と頼むと、アプリはブラウザを認可サーバーへ向かわせます。第9章の認可リクエストと同じ形ですが、client_id の値が番号ではなく、さっきの URL になっています。

一緒に送るものは第9章と変わりません。認可コードを返してほしい戻り先の redirect_uri、PKCE(コードの横取りを防ぐ仕組み)の code_challenge、state などです。図では、URL の札とこれらの札が、矢印に沿って認可サーバーの手前まで運ばれます。自己紹介の文書は、左下のとおりアプリのサイトに置かれたままです。

認可サーバーはこの時点で、このアプリに一度も会ったことがありません。手元の登録簿を探しても見つからないので、client_id が URL の形をしていることに気づいて、次の手を打ちます。

  • 認可リクエストに、URL を入れて送る
  • 人
  • アオイ
  • https://assist.example/client.json
  • ブラウザ
  • サーバー
  • 認可リクエスト
  • redirect_uri
  • code_challenge
  • AIアシスタント
  • 認可サーバー
  • client.json client_name: AIアシスタント redirect_uris: …/callback
  • client_id の値が、番号ではなく URL

認可サーバーが URL を取りに行く

CIMD に対応した認可サーバーは、client_id の URL へ自分で HTTP の GET を送り、自己紹介の JSON を取ってきます(図の①と②)。アプリが JSON を直接手渡すのではなく、認可サーバーが その URL の持ち主のサイト から取ってくるところが要です。https の URL から取れたということは、assist.example というドメインを管理している人が置いた文書だと言えます。第7章の証明書が、ここで効いています。

取りに行くときの作法も決まっています。応答が 200 以外なら失敗として扱い、HTTP のリダイレクト(別の URL への転送)は自動では追いかけません。転送を追うと、URL の持ち主ではない場所の文書を読んでしまうおそれがあるからです。

取ってきた文書は、HTTP のキャッシュの指示に従って一定時間とっておけるので、毎回取りに行く必要はありません。ただし失敗した応答や不正な文書はとっておきません。

  • 認可サーバーが URL を取りに行く
  • 人
  • アオイ
  • https://assist.example/client.json
  • ブラウザ
  • サーバー
  • ① GET
  • AIアシスタント
  • 認可サーバー
  • client.json client_name: AIアシスタント redirect_uris: …/callback
  • ② JSON(200 応答)
  • 200 以外は失敗。リダイレクトは自動で追わない

照合その1:取得した URL と、中の client_id

文書を受け取った認可サーバーは、まず二つの文字列を見比べます。一つは、リクエストで受け取って実際に取りに行った URL。もう一つは、取ってきた JSON の中に書かれた client_id です。図のように、この二つが一字一句同じでなければなりません。認可サーバーは大文字と小文字をそろえるといった「正規化」をせず、単純な文字列として比べます。

なぜこれが要るのでしょう。だれかが本物のアプリの JSON をまるごと写して、自分のサイトの https://evil.example/client.json に置いたとします。中の client_id は本物の URL のままなので、取りに行った URL と食い違い、ここで弾かれます。中身を書き換えて辻褄を合わせれば、今度は client_id が evil.example のものになり、本物のアプリにはなりすませません。こうして「この文書は、この URL のための自己紹介だ」と確かめられます。

照合に失敗したときは、認可サーバーはエラーを出し、ブラウザをアプリの戻り先へは送りません。正体の分からない相手に結果を返さないためです。

  • 照合その1:取得した URL と、中の client_id
  • 人
  • 取得した URL
  • アオイ
  • https://assist.example/client.json
  • OK
  • JSON の中の client_id
  • ブラウザ
  • https://assist.example/client.json
  • 完全に一致
  • サーバー
  • AIアシスタント
  • 認可サーバー
  • client.json client_name: AIアシスタント redirect_uris: …/callback
  • 文字列のまま比べる。食い違えばエラーで、戻り先へは返さない

照合その2:戻り先が一覧にあるか

次に、リクエストに付いてきた redirect_uri が、JSON の redirect_uris の一覧にあるかを確かめます。第9章で見たとおり、認可コードは戻り先へ届けられるので、ここがずれるとコードを他人のサイトへ送ってしまいます。そこで比べ方は、一覧のどれかと文字列が完全に一致すること(RFC 9700 が求める厳密な一致)です。図の札が入れ替わり、今度は戻り先どうしを比べています。右下の https://evil.example/cb は一覧にないので拒否されます。

例外は、利用者のパソコンの中で動くアプリが使う http://127.0.0.1 のような ループバック(自分自身を指すアドレス)の戻り先です。起動のたびに空いているポート番号が変わるので、ポート番号だけは違ってもよいとされています。これは版 -03 で書き足された点です。

ここまでの二つの照合で、認可サーバーは「この URL のアプリが、この戻り先で結果を受け取りたがっている」と確かめられました。

  • 照合その2:戻り先が一覧にあるか
  • 人
  • リクエストの redirect_uri
  • アオイ
  • https://assist.example/callback
  • OK
  • JSON の redirect_uris
  • ブラウザ
  • https://assist.example/callback
  • 一覧にある
  • サーバー
  • AIアシスタント
  • 認可サーバー
  • client.json client_name: AIアシスタント redirect_uris: …/callback
  • https://evil.example/cb
  • 一覧にない → 拒否
  • ループバックの戻り先だけは、ポート番号の違いを許す

あとは第9章の OAuth の流れ

照合が済めば、ここから先は第9章で見た OAuth の流れそのままです。認可サーバーはアオイ本人を確かめ(ログイン)、同意画面を出します。そこには自己紹介の JSON から読んだ client_name と、client_id の URL のドメインが並びます。アオイが許可すると、認可コードが戻り先へ届けられます(図の黄色の矢印)。

アプリは認可コードと、PKCE の code_verifier(最初に送った code_challenge の元になった秘密の値)をトークンエンドポイントへ送り、アクセストークンを受け取ります(オレンジの矢印)。このときに名乗る client_id も同じ URL です。アクセストークンを MCP サーバーなどの API に見せて使う流れも、これまでと変わりません。

つまり CIMD が変えたのは「初めてのアプリをどう知るか」という入口だけで、同意・コード・PKCE・トークンという本体には手を加えていません。

  • あとは第9章の OAuth の流れ
  • 人
  • 同意:AIアシスタント(assist.example)
  • アオイ
  • 認可コード
  • ブラウザ
  • サーバー
  • コード+code_verifier
  • アクセストークン
  • AIアシスタント
  • 認可サーバー
  • client.json client_name: AIアシスタント redirect_uris: …/callback
  • CIMD が変えたのは入口だけ。本体は第9章のまま

公開文書に、秘密は書かない

自己紹介の JSON は、だれでも読める公開文書です。ですから秘密は書きません。草案は client_secret(第9章で見たクライアントの合言葉)と client_secret_expires_at を使ってはならないと定め、合言葉を共有する方式のクライアント認証(client_secret_basic など)も認めていません。秘密鍵そのものも、もちろん載せません。

では、秘密を守れる 機密クライアント(サーバーで動くアプリなど)は、どうやって自分を証明するのでしょう。答えは第4章・第6章の公開鍵と署名です。アプリは公開鍵を jwks(鍵そのもの)か jwks_uri(鍵の置き場所)のどちらか一方で文書に示し、対応する秘密鍵は図の左下のとおり手元に置きます。トークンを受け取るときには、秘密鍵で署名した短い JWT(第8章)を送り、認可サーバーは文書の公開鍵でそれを検証します。これが private_key_jwt という方式で、版 -03 ではこの方式を名乗るなら jwks か jwks_uri が必須だと明記されました。

覚えておきたいのは、公開文書が見つかったことと、リクエストの送り主がそのアプリであることは別だという点です。URL はだれでも書けるので、送り主を確かめるにはこの署名のような手段が要ります。秘密を持たないパブリッククライアントでは、戻り先の照合と PKCE が守りの中心になります。

  • 公開文書に、秘密は書かない
  • client.json(公開) client_name redirect_uris jwks_uri
  • ブラウザ
  • 公開鍵(jwks_uri)
  • client_secret
  • バツ
  • 載せない
  • AIアシスタント
  • サーバー
  • 秘密鍵
  • 秘密鍵で署名した JWT
  • アプリの手元だけ
  • 認可サーバー
  • 文書が見つかっても、送り主の証明にはならない

URL が本物でも、アプリが善良とは限らない

照合が通って分かるのは、「この文書は、この URL を管理している人が出した自己紹介だ」ということまでです。そのアプリが善良かどうかは別の問題です。client_name やロゴ(logo_uri)は自己申告なので、なりすます人が自分のドメインに、有名なアプリの名前を書いた文書を置くこともできます。

そこで頼りになるのが、自己申告ではない ドメイン名 です。草案は、認可サーバーが同意画面に client_id の URL のホスト名(図の緑の枠の assist.example)をはっきり表示するよう勧め、文書の取得に失敗したときもホスト名は見せるべきだとしています。ロゴは認可サーバーが前もって取ってきて画像であることを確かめ、自分のサーバーから出すことになりました(版 -03)。表示名の扱いに独自の方針を決めてもかまいません。

認可サーバーは、信頼するドメインだけを受け付ける許可リストや、初めて見るドメインへの警告といった方針も持てます。また MCP の仕様は、戻り先が localhost だけのアプリに注意を促しています。他人が本物のアプリの URL を名乗り、自分のパソコンの localhost で待ち受けても、文書だけでは見分けられないからです。

  • URL が本物でも、アプリが善良とは限らない
  • 人
  • 写真
  • AIアシスタント
  • アオイ
  • 自己申告
  • URL から分かるドメイン
  • ブラウザ
  • assist.example
  • サーバー
  • 予定の読み取りを許可しますか?
  • AIアシスタント
  • 認可サーバー
  • 許可
  • 拒否
  • 人
  • 注意
  • なりすます人
  • 名前とロゴは自己申告。ドメインは URL が示す

取りに行く側の注意:SSRF と対応の確認

CIMD では、認可サーバーが 外から渡された URL へ自分で取りに行きます。ここを悪用されると、SSRF(サーバー側リクエストフォージェリ)という攻撃になります。なりすます人が、外からは届かない社内の管理用の窓口を指す URL を client_id に入れると、認可サーバーが内側からそこへアクセスしてしまうかもしれません。図の盾は、その取得を止めるところです。

草案は、URL のホスト名を IP アドレスに引いた結果が、社内用やループバックなどの 特別な用途の IP アドレス(RFC 6890)になるなら取りに行ってはならない、と定めています。文書の中の jwks_uri などの URL も同じ扱いです。ほかにも、リダイレクトを追わないこと、読み込む量を絞ること(目安は 5 キロバイト)、キャッシュで取りに行く回数を抑え、必要なら回数の制限をかけることが挙げられています。認可サーバー自身の issuer(第10章で見た発行者の名前)と同じ URL を名乗る client_id も受け付けないようにします。

アプリの側は、相手が CIMD に対応しているかを、認可サーバーのメタデータ(RFC 8414)の client_id_metadata_document_supported が true かどうかで確かめます。対応していない相手には、事前登録など別の方法を使います。

  • 取りに行く側の注意:SSRF と対応の確認
  • 内部ネットワーク
  • https://intra.example/admin
  • データベース
  • 人
  • 注意
  • サーバー
  • 守られている
  • この URL を見て
  • 管理用の窓口
  • なりすます人
  • 認可サーバー
  • 認可サーバーのメタデータ(対応の印)
  • "client_id_metadata_document_supported": true
  • 特別な用途の IP には取りに行かない。転送を追わず、読む量も絞る

まとめ:CIMD を公開すれば、ログインも完成する?

この章では、client_id を「アプリの自己紹介が置かれた URL」にする CIMD を見てきました。アプリは JSON を自分のサイトに公開し、認可サーバーはその URL を取りに行って、URL と中の client_id、戻り先と一覧を照合します。秘密は文書に書かず、送り主を証明したいときは公開鍵と署名を使います。名前やロゴは自己申告なのでドメインを見せ、取りに行く側は SSRF に備えます。

では、CIMD の文書を公開すれば、利用者のログインまで出来上がるのでしょうか。答えは「いいえ」です。CIMD が解決するのは、認可サーバーが初対面のアプリを 知る 方法だけです。図のように、その後ろには照合、アオイ本人の確認と同意、認可コードと PKCE、トークンの発行と API での検証が、これまでどおり続きます。ログインの結果をアプリに伝えたいなら、第10章の OIDC の ID トークンが要ります。

CIMD はまだ草案ですが、MCP の認可の仕様が 2025-11-25 版から対応を勧め(SHOULD)、2026-07-28 版では動的クライアント登録を非推奨にしたことで、使われる場面が増えています。ただし MCP の仕様が参照しているのは草案の版 -00 です。作るときは IETF の最新版と、相手の認可サーバーの対応状況を確かめてください。

  • まとめ:CIMD を公開すれば、ログインも完成する?
  • CIMD アプリを知る
  • 照合 URL・戻り先
  • OAuth 同意・コード・トークン
  • OIDC ログインの結果
  • この章
  • 第9章・第10章の手順が、これまでどおり続く
  • 答えは「いいえ」。CIMD が担うのは、アプリを知る入口だけ

本全体のまとめ:11の章はこうつながっていた

最後に、本全体を振り返ります。出発点は第1章の二つの問いでした。「相手は本当にその人か」(認証)と、「その人に何をさせてよいか」(認可)です。この問いに答えるための道具を、第2章から第7章でそろえました。データの指紋であるハッシュ、同じ鍵で閉じて開く共通鍵暗号、閉じる鍵と開く鍵を分けた公開鍵暗号、その二つを組み合わせて鍵を安全に分け合うハイブリッド暗号、秘密鍵で作者を示す電子署名、そして「この公開鍵はだれのものか」を認証局が保証する証明書です。

第8章からは、その道具を組み合わせた仕組みでした。JWT は、署名で改ざんを見抜ける札に情報を載せる形式です。OAuth は、パスワードを渡さずにアプリへ権限を預け、アクセストークンという札で API を使わせます。OIDC は、その流れに「だれがログインしたか」を伝える ID トークン(署名付きの JWT)を足しました。そして CIMD は、初めて会うアプリを、証明書で守られた https の URL と公開鍵で知る方法でした。図の線のとおり、後ろの章は前の章の部品の上に建っていて、その先にはいつも、安心して使いたい利用者のアオイがいます。

新しい仕組みに出会ったときも、「何を守りたいのか」「どの部品で守っているのか」を問えば、たいてい分解して理解できます。この本が、その手がかりになればうれしいです。

  • 本全体のまとめ:11の章はこうつながっていた
  • 道具をそろえる(第1〜7章)
  • 認証と認可 本人と権限
  • ハッシュ データの指紋
  • 共通鍵暗号 同じ鍵で守る
  • 公開鍵暗号 鍵を二つに
  • ハイブリッド 鍵を共有する
  • 電子署名 作者の証明
  • 人
  • CIMD アプリを知る
  • OIDC ログイン結果
  • OAuth 権限を預ける
  • JWT 署名付きの札
  • 証明書 鍵の持ち主
  • アオイ
  • 道具を組み合わせた仕組み(第8〜11章)
  • 後ろの章は、前の章の部品の上に建っている

0 件のコメント

読み込み中…

権利侵害・不適切な内容の連絡: [email protected]