世界を動かす技術を、日本語で。

OAuthのイラスト付きガイド

概要

  • OAuth は2007年に Twitter で誕生
  • 第三者アプリ が安全にユーザー代理で操作する仕組み
  • アクセス・トークン が中心的役割
  • ユーザー同意認可コードフロー が基本構造
  • セキュリティ設計 と用語整理が理解の鍵

OAuth誕生の背景と課題

  • 2007年、Twitter でOAuthが誕生
  • 第三者アプリ がユーザー代理でツイート投稿を希望
  • 当時は ユーザー名・パスワード をアプリに直接入力する方式が主流
  • この方法は パスワード漏洩悪用リスク が高い
  • APIキー も存在したが、ユーザー単位での制御ができず不十分

OAuthの基本的な仕組み

  • OAuthアクセス・トークン を中心に設計
  • アプリはユーザーごとに 固有のトークン を取得
  • ユーザーの代理で安全に操作やデータ取得が可能
  • 複数の利用パターン があり、理解が難しくなりがち

OAuthフローのユーザー体験例

  • YNAB (家計簿アプリ)と Chase Bank の連携例
    • YNABからChaseへのリダイレクト
    • Chaseでログインとアクセス許可(例:口座選択)
    • ChaseからYNABへリダイレクト
    • YNABがChaseデータにアクセスできる状態

OAuthフローの裏側

  • 最終目標は OAuthクライアント (例:YNAB)が アクセス・トークン を取得すること
  • 認可サーバー(例:Chase)はリダイレクト時に 認可コード を付与
  • アプリは バックエンドから認可サーバーへPOST し、認可コードと クライアントシークレット を送信
  • サーバー間通信で アクセス・トークン を安全に取得

OAuthの用語整理

  • リソースオーナー :ユーザー本人
  • OAuthクライアント :連携アプリ(例:YNAB)
  • 認可サーバー :ログインや認可を担当(例:Chase)
  • リソースサーバー :ユーザーデータを管理(認可サーバーと同一の場合あり)
  • スコープ :許可する操作やデータ範囲

OAuthの開発者視点

  • アプリ登録が必要
    • アプリ名リダイレクトURI を認可サーバーに登録
    • クライアントID (公開用)と クライアントシークレット (秘密)を取得
  • OAuthフロー開始時
    • クライアントID、リダイレクトURI、レスポンスタイプ(通常は"code")、スコープを付与して認可サーバーへリダイレクト
  • 認可サーバーはリクエストを検証し、ユーザーに同意画面を表示

リダイレクトURIとセキュリティ

  • リダイレクトURI はアプリ登録時に限定
  • 悪意のあるサイトへのリダイレクトを 防止
  • モバイルアプリの場合は カスタムスキームURI (例:myapp://foobar)も利用可能

認可コードとトークン交換

  • 認可サーバーはリダイレクト時に 認可コード をURLで返却
  • アプリは 認可コードクライアントシークレット を認可サーバーへPOSTし、 アクセス・トークン を取得
  • 各ステップでセキュリティ上の考慮が徹底

フロントチャネルとバックチャネル

  • OAuthでは フロントチャネル (GET/URLパラメータ)と バックチャネル (POST/暗号化通信)を区別
  • クライアントシークレットをフロント側で扱うと 漏洩リスク があるため、通常はバックエンドで交換
  • バックエンドがない場合は PKCE(Proof Key for Code Exchange) を利用
    • PKCEは クライアントシークレット不要 で、セキュリティを担保

OAuth設計の複雑さの理由

  • 度重なるセキュリティ強化 による仕様追加
  • ユーザー関与 (同意画面など)によるフロントエンド実装
  • フロントチャネルの脆弱性バックチャネルの安全性 を両立する工夫

このように、OAuthは 安全性ユーザー主体の同意 を両立するために設計された認可フレームワーク。セキュリティの観点から各ステップに多層的な対策が施されている点が特徴です。

Hackerたちの意見

「セッションフィクセーション」やトークンハイジャック攻撃のグラフィックはどこにあるの?1.0の歴史とOAuth 1.0aを急いで出したことは、ずっと覚えてる。2008年のことで、俺たちヤマーレンジニアがこの新しいベストプラクティスの認証システムを実装したんだ。それがライブになったんだけど、数日後にオフィスの誰かがそのハイジャックが可能だって証明しちゃったんだよね。

それが何の関係があるの?今はOAuth 2.0なんだから、17年前のことなんて誰が気にするの?

今、OAuthとOIDCを実装してるんだけど、こんなシンプルな概念なのに、実際に実装するための情報を得るのがめっちゃ難しい。なんでだろう?どこを見ても表面的なことしか書いてなくて、実際にコードに実装できる具体的な情報が全然ないんだ。結局、仕様書をひたすら見て、Grokがいろんなことの意味を説明してくれてめっちゃ助かった。情報が不足してたり、ドキュメントや仕様の奥深くに埋もれてたりするところを理解するのに役立ったよ。これが初めて、こういう新しい「AI」を本当にありがたいと思った瞬間だった。普段は全然使ってないけど。

認証サーバーを実装してるの?それとも既存のものと統合してるの?どちらにせよ、前回このトピックを掘り下げたとき、同じように感じたよ。ウェブには表面的なことしか書いてない記事がたくさんあって、基本的なことだけしかカバーしてない。詳細が抜けてることが多くて、俺の経験ではそれが理解を難しくする原因になった。言った通り、RFCやOIDCの仕様を直接追うのが一番役立ったよ。もし認証サーバーを実装してるなら、既存の実装を見てみるのもいいかも。Duende IdentityServer(https://github.com/DuendeSoftware/products/tree/main/identit...)が.NETの分野では最も広く使われてるやつだよ。

OAuth2(認可コードグラント、両サイド)を実装したとき、このガイドがかなり役立ったよ:https://alexbilbie.github.io/guide-to-oauth-2-grants/ 一つ気づいたことがあって、理論的にはリフレッシュトークンは期限切れにならないはずなのに、多くのサイトでは期限切れになるんだ。使えるリフレッシュトークンを維持するために、時々リフレッシュしなきゃいけない。多くの人が「ライブラリを使えばいい」と言うけど、OAuthとアプリの接触面がかなり広いから、ライブラリがあまり役立たないこともあるんだ。これが理由の一つで、自分で実装を書いたんだ(Clojureで)。

そうそう、OpenPubkeyのためにOIDCを深く掘り下げる必要があったんだけど、自分用の教材やメモを作ることになった。何かがどう動くのか混乱したときに毎回参考にするGoogleドキュメントのスライドがたくさんあったよ。OpenPubkeyの論文を書く大きな動機は、OIDCがどう機能するかの詳細なメモを持っておくことだった。OIDCのパブリッククライアントを理解しようとしている人には、OpenPubkey論文のセクションIとIIをおすすめするよ。私は少なくとも月に一度は参照してる: https://eprint.iacr.org/2023/296

OAuthは、委員会で設計されたJSON付きのSAMLに過ぎないから、全ての機能が揃っていて、オプションが多いし、誰と統合するか、どうするかによって変わるんだよね。

これは、システムの多くの部分が複数のIETF RFCに分散しているためです。OAuthは時間とともに改善されて、より安全になってきました。重要な部分をすべてまとめてOAuth 2.1にする努力が進められています。高い信頼性が求められるユースケースについては、FAPI 2.0のセキュリティプロファイルを見てみてください。

OAuthは、仕事で何度も勉強して、実装して、再実装してきたけど、いつも忘れちゃうんだよね。今はRFC6749のコピーをデスクの近くに置いて、ハイライトしてる。たまに細かいところを見に行く必要があるし。あと、OpenIDの仕様書は印刷用に整形されてない文書がたくさんあって、作者たちが誰も実際に読むとは思ってないんじゃないかって感じる。

このページ:https://infosec.mozilla.org/guidelines/iam/openid_connect.ht... は、OIDCの実装をしていたときに見つけた中で一番役に立った情報だった。

理由はよくわからないけど、これが私の経験で、基本的にインターネットの80%がこんな感じ。何かをする方法を共有する記事が、ほとんどの部分で興味のないことを話して、やっと複雑な部分にたどり着いたと思ったら、細かいところを飛ばしたり、使いたくないライブラリを使ったりして、「はい、できた!やったね!」って感じになる。

これいいね!ビアガーデンのアナロジーを思い出すなぁ(https://www.stevetecharc.com/blog-posts/understanding-oauth-...)。これもOAuthへの良い入り口だと思った。

「使おうとしている例はYNABです。使ったことがないなら、YNABはMintの有料版みたいなものです。このアナロジーが使われて嬉しい…だって、俺もOAuthとオープンバンキングが実質的に同じだと思ってる。これって俺だけの考えなのかな?それとも、実際には二つの異なるものなの?」

これはよく書けてて、かなり理解が深まったよ。PKCE版もあるといいな、今や一般的で推奨されてるから。

サーバー側かクライアント側の実装を考えるかによるけど、RFCの例のセクションは結構役立ったよ。https://datatracker.ietf.org/doc/html/rfc7636#appendix-B ただ、コメントを書く準備をしているときに引っかかったのは、両方のパラメータがすごく似た名前だったことかな。:-( クライアントが将来の知識を証明するためのやり取りだと考えることができるけど、実際にはトークンの引き換え時に保持していた「秘密」を送信するだけなんだ。ランダムな整数1を使ってみよう。1. r = randomInt() 2. future = base64(sha256(r)) 3. /authorize?client_id=cid-123&code_challenge_method=S256&code_challenge=${future} サーバーは${future}を耐久性のあるストレージに保持する必要がある。なぜなら、ステップ5で返す${code}にアクセスする必要があるから。4. <-- &code=abc789 5. /token?code=abc789&code_verifier=${r} これで、サーバーはステップ2の同じsha256の処理を繰り返して、クライアントが「r」を提示することで、ステップ3のリクエスターが本当に同じだったことを確認できる。なぜなら、同じIPアドレスでクライアント_idをトランジット中に嗅ぎ取った他の誰も、「r」の十分にランダムな値を知ることはできなかったから。これでコード交換中に彼らのキーを証明できるんだ。

フロントチャネルとバックチャネルについての部分はちょっと違うと思う。GETリクエストとPOSTリクエストはどちらもHTTPSで暗号化されてるよ。URLも含まれてるけど、ドメインは別でDNS解決が行われるからね。フロントチャネルとバックチャネルは、信頼の境界や、クライアントの視点から見たときに何が公開情報で何がプライベート情報かに関係してるんだ。

うん、全然意味がわからなかった。POSTが「見えない」から安全だって考える人を見たことないよ。隠すことで安全性を確保するってやつね…

重要なポイントは、URLがブラウザの履歴に保存されていて、決してプライベートではないということ。

URLは通常ログに記録されるし、他のコメント者が指摘したように、ブラウザの履歴やブックマークに保存されることもある。パラメータのURLエンコーディングを避ける一般的な推奨を見たことがあるけど、たぶんそれが理由かな?

これ、めっちゃいいOAuthの説明だね。素晴らしい!

個人的にはOAuth2はすごく設計が悪いと思う。いくつかの構造的な問題があるし、「このOAuthプロバイダーに接続」するとメインアカウントをハイジャックされるし、リダイレクトハイジャックでリファラーを通じて認証コードが漏れたり、#hashパスでaccess_tokenが漏れたりする。さらに、「state」CSRFトークンはオプションで、通常は無視されるし… それについての古いまとめと解決策があるよ。https://sakurity.com/oauth - 認証プロトコルに興味があれば、LLMで分析するのがいいかも。

あなたのコメントはすごく省略されていて、ほとんど理解できないよ。理解できないから、かなりダウンボートされてるんじゃないかな。LLMについてのコメントもあんまり役に立ってないし。

イラストが大好き!素晴らしい説明、ありがとう!