feature image

2020年4月3日 | ブログ記事

猫でもわかる(諸説)OAuth 2.0【新歓ブログリレー2020 26日目】

はじめに

こちらは新歓ブログリレー2020 26日目の記事です。
こんにちは。19Bのmazreanです。traP内ではSysAd班に所属しており、普段はサーバーサイドのコードを書いたり、サーバー触ったりしています。

traP内には、traQという部内製のチャットサービスがあります。traPでは原則入部時にこのチャットに登録することになっています。そうなると他のサービスを作るにあたってこのアカウントを使ってユーザー認証をしたり、ここから情報を得たりしたいことがでてくるわけです。この記事では、そのようなときに使うOAuth 2.0という仕組みについて説明していきたいと思います[1]

OAuth 2.0とは

簡単なイメージ

まず、いきなりOAuth 2.0と言われてもイメージが湧きづらいと思います。インターネットを使っているとTwitter「〜にアカウントの利用を許可しますか?」とかができるものがあると思います。これを可能にしているのがOAuth 2.0というものです(traPでは「〜にtraQアカウントの利用を許可しますか?」みたいなことができるようにしています)。人によっては、このようなときに「Twitterなどが他のサイトに自分のパスワードとかを渡している」と思っているかもしれませんが、そのようなことはしていません(そんなことしていたら大問題になっています)。パスワードをTwitterなどから漏らすことなく、「〜にアカウントの利用を許可しますか?」のようなことを可能にするための仕組み、それがOAuth 2.0というものです。

もう少し具体的な説明

次に、OAuth 2.0とはどのようなものかもう少し具体的に説明していきます。
OAuth 2.0とは認のための仕組みです。もともとの目的は認ではありませんが、やり方次第ではにも利用できます。といっても「認?認?何か違うの?」となると思うのでそれぞれ説明していきます。

このうちの「認可」を行うことにより「〜にtraQアカウントの利用を許可しますか?」のようなことができるようにするのが、OAuth 2.0というものです。つまり、OAuth 2.0では

ということが必要になります。また、この結果、OAuthの種類によっては「認証」も行える場合があります。

OAuth 2.0の基本的な流れ

OAuth 2.0には、

前提

解説の前に、一つ前提があります。それは、通信ではすべてhttpではなくhttpsを使用しており、中間者攻撃[3]を受けることはないということです。

Implicit Flow

これは主にクライアントのみの簡単なアプリケーションで使われるFlowです。流れ、特徴の順で解説していきます。

流れ

  1. ブラウザで認証画面を開く
    アプリケーションでログイン確認をしたときにアプリケーションがAccess Tokenを持っていなければ、ブラウザでOAuthで情報を取得したいアカウント(Twitterアカウントなど)の認証画面(ログイン)を開きます。
  2. 認証
    ユーザーが1.で表示された認証画面でパスワードなどの認証情報を入力し、Twitterなどにログインします。
  3. 同意画面
    ログインが終わると認可サーバーが同意画面を返し、ブラウザでアプリケーションがTwitterなどから情報を得ることを許可することへの同意画面が開かれます。
  4. 同意
    ユーザーが3.で表示された同意画面から同意し、認可サーバーにそのことを伝えるリクエストを飛ばします。ここでユーザーが、アプリケーションへ一部の情報へのアクセス権を与えることを許可したことが確認できます。
  5. Access Tokenをつけてリダイレクト
    URIにクエリパラメーターなどでAccess Tokenの情報をつけてリダイレクトします。ここでアプリケーションはAccess Tokenを手に入れることができ、認証・認可が完了します。

implicit-7

アプリケーションは、Twitterなどのエンドポイントを叩くときにここまでで得たAccess TokenをHeaderに付けてリクエストを飛ばします。Twitterなどは正しいAccess Tokenのついていないリクエストは弾くことで、認可されたアプリケーションにのみ情報を渡すことができます。

特徴

クライアントのみで実装できる

流れの図でアプリケーションのBack-Endがでてこなかったとおり、このFlowではBack-Endの実装が必要ありません。このため、サーバーを利用しないアプリケーションの認可に向いています。

実装が楽

このFlowでアプリケーションのClientがやっていることは
ブラウザに認証画面を開かせる
リダイレクトされて渡されたAccess Tokenを受け取る
ということだけです。これは他のFlowと比べて実装が容易です。また、仕組み自体も他のFlowと比べて簡単なので、理解が容易です。

Access TokenがClientにある

このFlowではAccess Tokenの取得の中でブラウザからアプリケーションへのリダイレクトが使われています。アプリケーションがAndroidやiOSなどのネイティブアプリケーション出会った場合、このリダイレクト時にうまくやると[4]他のアプリケーションがAccess Tokenを取ることもできます。また、Webアプリケーションだった場合でもユーザーはリダイレクト中にAccess Tokenを取ることが可能です。仮にこのAccess Tokenが他のアプリケーションや他のユーザーへ渡された場合、

などのことが可能になります。このため、Implicit Flowはセキュリティの強度が低いとされています。

認証には使ってはならない

このFlowは絶対に認証に使ってはなりません。このFlowを認証に使うと重大なセキュリティ上の問題が生じます。どういうことか説明していきます。
あるアプリケーションA,Bがあったとします。A,Bは、ある共通の認証サーバーを使っています。また、Aの所有者は悪意のある人間Xであったとします。Bは認にImplicit Flowを使っているとします。ユーザーYがアプリケーションAでImplicit Flowをします。このとき、アプリケーションAは当然ユーザーYとしてのAccess Token(これをαとします)を持っています。アプリケーションAはXの管理下にあるためXも自由にこのαを抜き取り、入手することができます。次にXがアプリケーションBへImplicit Flowでログインします。この中のブラウザからアプリケーションBへのリダイレクトでXはAccess Tokenをαへ置き換えます。そうするとどうなるでしょうか?XがYとして認証され、アプリケーションBにあるYの情報を盗むことができます。
Implicit Flowを認証に使うと、このような重大なセキュリティホールが生まれるため、絶対に認証に使ってはいけません。

Authorization Code Flow

流れ

  1. ブラウザで認証画面を開く
    認証・認可がされていない場合、アプリケーションのBack-EndからクライアントへのレスポンスでClientに、ブラウザでTwitterなどの認証画面を開くよう指示をします。
  2. 認証
    ユーザーがパスワードなどの認証情報を入力し、Twitterなどにログインします。
  3. 同意画面
    ログインが終わると認可サーバーが同意画面を返し、ブラウザでアプリケーションがTwitterなどから情報を得ることを許可することへの同意画面が開かれます。
  4. 同意
    ユーザーが3.で表示された同意画面から同意し、認可サーバーにそのことを伝えるリクエストを飛ばします。ここでユーザーが、アプリケーションへ一部の情報へのアクセス権を与えることを許可したことが確認できます。
  5. Authrization Codeをつけてアプリケーション(Client)へリダイレクト
    認可サーバーからのレスポンスでブラウザにクエリパラメーターなどにAuthorization Codeというもの(詳しいことは後述)をつけてアプリケーションのClientへリダイレクトします。
  6. Authorization Codeをアプリケーション(Back-End)へ渡す
    クエリパラメーター、リクエストボディなど、何らかの方法でAuthorization CodeをつけてアプリケーションのClientからアプリケーションのBack-Endへリクエストを飛ばします。これにより、アプリケーションのBack-EndへAuthorization Codeの値を渡します。
  7. Authorization Codeを使い、Access Tokenを取得する
    アプリケーションのBack-Endから6.で手に入れたAuthorization Codeをリクエストボディに含めて認可サーバーへリクエストを飛ばします。
  8. 認可サーバーがAccess Tokenを返す
    認可サーバーでリクエストボディに含まれるAuthorization Codeが5.でつけたものと同じか確認し、合致すればAccess TokenをアプリケーションのBack-Endへレスポンスとして返します。
  9. ログインセッションを発行する
    Implicit Flowと違い、Authorization Code FlowではBack-EndにAccesss Tokenが保存されています。このため、Access Tokenを発行したあとにClientとAccess Tokenの紐づけが必要です。このため、Clientにセッションを発行します。

authorization-4

Implicit Flowと同様、ここまでで得たAccess TokenをHeaderにつけてリクエストを飛ばすことでアプリケーションのBack-EndはTwitterなどのエンドポイントを叩くことができます。ClientでTwitterなどのエンドポイントから得た情報を使いたい場合はBack-EndがTwitterなどから得た情報をClientへ返すという形を取ることになります。

Authorization Codeとは

Authorization Code Flowの流れを説明する中ででてきたAuthorization Code。これの意義を説明していきます。流れの図を見て確認するとわかりやすいと思いますが、このFlowではAccess Tokenが一切Clientを経由していません。このため、Back-Endに脆弱性が存在しなければ盗まれる可能性があるのはAuthorization Codeのみとなります。また、Authorization CodeとAccess Tokenの引き換えは1度しか行うことができないようになっています。これによって、1度Back-EndがAuthorization CodeとAccess Tokenの引き換えを行えば、事実上Access Tokenを他のアプリケーションや人間が得ることは不可能になります。これを実現することがAuthorization Codeの目的です。

特徴

比較的セキュリティ強度が高い

上述の通り、Authorization Codeを使うことでAccess TokenがClientを通ることがなく、また、Authorization Codeは一度使われると無効化されるため、Implicit Flowと比べてAccess Tokenが盗まれづらく、セキュリティ強度が高いです。とは言っても、このままだと問題があるので、その問題を塞ぐための仕組みがあります。これについては次の章で説明していきます。

そのままでは認証に使えない

このFlowもImplicit Flowと同様にそのままでは認証に使うことができません。Implicit Flowで認証をした場合にはAccess Tokenを置き換えることで他人へのなりすましができましたが、置き換えるものをAuthorization Codeに変えるだけでこのFlowでも同様のことができます。ただ、Authorization Code Flowの場合はこれに対する対策が可能で、この対策をした場合には認証に使うこともできます。対策についてはこのあと述べていきます。

問題点の整理

Authorization Code FlowはImplicit Flowと比べると安全です。しかし、このままでは問題があります。

  1. 他のユーザーに自分としてエンドポイントを叩かせることができる
    ブラウザからアプリケーションへのリダイレクトを悪意のあるユーザーが止めたとします。そして、このときのリダイレクト先URIを他人に踏ませる[4:1]と、他人を自分としてエンドポイントを叩かせることができます。認可の仕組みとしてみたとき、これができると他人がした操作が自分として扱われ、自分が見ることができるようになるため問題です。

  2. 他のアプリケーションがAuthorization Codeを横取り・流用できる
    Implicit Flowでも説明したとおり、ブラウザからアプリケーションへのリダイレクトではうまくやると値を横取りすることができます。Authorization Code Flowでこれをされると、他のアプリケーションがAuthorization Codeを横取りし、それをAccess Tokenと引き換えることで本来認可されていないアプリケーションが認可されてしまいます。これがされてしまうと、OAuth 2.0の目的である認可ができなくなるため、問題です。

解決策

上記の問題が放置されては問題です。このため、OAuth 2.0ではこれらができないようにするための仕組みが用意されています。

state

これは1.への対策です。
「Clientに、ブラウザでTwitterなどの認証画面を開くよう指示」のところでセッションにstateというキーでランダムの文字列Sを保存します。そして、リダイレクト先のクエリパラメーターにstateというキーでSをつけます。こうするとブラウザからアプリケーションへのリダイレクトでstateという値がAuthorization Codeと同様の形でつけられます。この値がセッションに保存されているものと一致するかアプリケーションのBack-Endでチェックします。セッションはクライアントに紐付けられているので、これにより他のユーザーがAuthorization Codeを使うことができなくなり、1.のようなことができなくなります。

PKCE(Proof Key for Code Exchange by OAuth Public Clients)

これはもともと2.の対策なのですが、結果的に1.への対策になってもいます。そのため、こちらへ対応していればstateがなくとも問題ありません。また、これをすることにより他のアプリケーションのAuthorization Codeを流用することもできなくなるので、Authorization Code Flowで認証を行うこともできます。以下では方法を説明していきます。
まず、「Clientに、ブラウザでTwitterなどの認証画面を開くよう指示」のところでセッションにcode_verifierというキーでランダムな文字列Sを保存します。そして、リダイレクト先のクエリパラメーターにcode_challengeというキーでSをハッシュ化した値、code_challenge_methodというキーでハッシュ化の方法をつけます。このようにすると、認可サーバーでcode_challenge、code_challenge_methodが紐付けられます。そして、「Authorization Codeを使い、Access Tokenを取得する」のときにセッションにあるcode_verifierをリクエストに含め、認可サーバーでcode_verifierをハッシュ化した値とcode_challengeを比較し、一致しなければAccess Tokenを返さないようにします。
こうすることで、他のアプリケーションのAuthorization Codeが使われたとしても紐付けられているのcode_verifierをハッシュ化した値とcode_challengeが一致しないため、2.やAuthorization Codeの置き換え、1.ができなくなります。

traPでのOAuth 2.0

「はじめに」で述べたとおり、traQにはOAuth 2.0の認可サーバーとしての機能が実装されています。
このOAuth 2.0ではAuthorization Code Flowのみに対応しており、state、PKCEが使えるようになっています。これにより、部内で開発されたツールでtraQにある情報を利用でき、わざわざそれぞれのサービスでログインをする必要がなくなっています。

まとめ

自分はOAuthについて理解するのにかなり時間がかかりました。今回の記事がそのような方の助けとなれば幸いです。


明日は@N君と@Suu_u君の記事です。お楽しみに!


(2020/04/11 03:47修正)
Implicit Flow > 特徴 > 認証には使ってはならない に

をしました。


  1. 新入生は「こんなことやっているんだぁ。凄そ〜。」ぐらいの感じで読んでいてください。自分が新入生のときだったら絶対に理解できていません(猫でもわかるとは…)。この記事を理解できないからといって「traPガチプロしかいなさそうだし入るのやめよう。」などとは絶対に思わないでください。自分はプログラミング未経験でしたが、現在traPで幸せに活動しています。 ↩︎

  2. あくまでも基本的なです。この2つを更に派生したもの、組み合わせたものなどもあります。 ↩︎

  3. 通信の途中では様々なルーターなどを経由することになります。httpという種類のプロトコルでは、内容が暗号化されていないためこのルーターなどがその気になれば内容を盗み見ることができてしまいます。このことを中間者攻撃といいます。 ↩︎

  4. メールで送りつけるなどがこの方法としてよく挙げられます。 ↩︎ ↩︎

mazrean icon
この記事を書いた人
mazrean

SysAd班で活動したり、百合漫画の布教をしたりしている人。

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2023年12月11日
DIGI-CON HACKATHON 2023『Mikage』
toshi00 icon toshi00
2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2023年7月15日
2023 春ハッカソン 06班 stamProlog
H1rono_K icon H1rono_K
2022年9月26日
競プロしかシラン人間が web アプリ QK Judge を作った話
tqk icon tqk
2024年3月15日
個人開発として2週間でWebサービスを作ってみた話 〜「LABEL」の紹介〜
Natsuki icon Natsuki
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記