OAuth 2.0 と OpenID Connect でクラウドベースのアプリケーションを保護する
OAuth 2.0 と OpenID Connect を使用してクラウドアプリケーションのセキュリティを確保し、認証と認可で優れたユーザー体験を提供するための完全ガイド。
はじめに
クラウドベースのアプリケーションは、現在のトレンドです。アプリケーションの種類はさまざまですが(ウェブ、モバイル、デスクトップなど)、いずれもストレージ、コンピューティング、データベースといったサービスを提供するクラウドバックエンドを持っています。これらのアプリケーションは、多くの場合、ユーザーを認証して特定のリソースにアクセスする権限を与える必要があります。
自家製の認証と認可メカニズムを作成することも可能ですが、クラウドアプリケーションの開発が進むにつれて、セキュリティが最も重要な懸念事項の1つとなっています。幸いにも、私たちの業界には、OAuth 2.0 や OpenID Connect のような実績のある標準があり、安全な認証と認可の実装を助けてくれます。
この投稿では、以下の前提があります:
- アプリケーション開発(ウェブ、モバイル、その他の種類)の基本を理解していること。
- 認証と認可の概念について聞いたことがあること。
- OAuth 2.0 と OpenID Connect について聞いたことがあること。
はい、「聞いたことがある」で十分です。この投稿では、実際の例を使用して概念を説明し、図を使ってプロセスを説明します。それでは始めましょう!
OAuth 2.0 対 OpenID Connect
OAuth 2.0 と OpenID Connect に精通している場合でも、このセクションで実際の例をいくつか紹介するので、引き続きお読みいただけます。この標準に不慣れな方も、簡単に紹介するので安全です。
OAuth 2.0
OAuth 2.0 は、アプリケーションがユーザーまたはアプリケーション自身に代わって他のアプリケーションの保護されたリソースへの限定的なアクセスを取得できるようにするための認可フレームワークです。Google、Facebook、GitHub などの多くの人気サービスは、ソーシャルログイン(「Google でサインイン」など)に OAuth 2.0 を使用しています。
例えば、ユーザーの Google Drive にアクセスしたい Web アプリケーション MyApp があるとします。ユーザーに Google Drive の資格情報を共有するよう依頼する代わりに、MyApp は OAuth 2.0 を使用して、ユーザーに代わって Google Drive へのアクセスを要求できます。以下は簡略化されたフローです:
このフローでは、MyApp はユーザーの Google Drive の資格情報を見ることはありません。代わりに、Google から アクセストークン を受け取り、それを使用してユーザーに代わって Google Drive にアクセスします。
OAuth 2.0 の用語では、MyApp は クライアント であり、Google は簡単化のため、認可サーバー と リソースサーバー の両方です。実際には、シングルサインオン(SSO)体験を提供するために、別々の認可サーバーとリソースサーバーを用意することがよくあります。例えば、Google は認可サーバーであり、Google Drive、Gmail、YouTube などの複数のリソースサーバーを持っている場合があります。
実際の認可フローはこれよりも複雑です。OAuth 2.0 には、異なる認可タイプ、スコープ、および他の概念が存在することを知っておくべきです。今はそれを一旦脇に置いて、OpenID Connect に進みましょう。
OpenID Connect (OIDC)
OAuth 2.0 は認可に最適ですが、ユーザーを識別する方法(つまり、認証)がないことに気づくかもしれません。OpenID Connect は認証機能を追加するための OAuth 2.0 上のアイデンティティレイヤーです。
前述の例では、MyApp は認可フローを開始する前にユーザーが誰であるかを知る必要があります。ここでは 2 人のユーザーが関与しています:MyApp のユーザーと Google Drive のユーザー。この場合、MyApp は自分のアプリケーショ ンのユーザーを知る必要があります。
ユーザーがユーザー名とパスワードで MyApp にサインインできると仮定した単純な例を見てみましょう:
私たちが自分のアプリケーションのユーザーを認証しているので、通常は OAuth 2.0 フローの Google のように許可を求める必要はありません。一方で、ユーザーを識別できるものが必要です。OpenID Connect はID トークンやuserinfo エンドポイントのような概念を導入して、それを支援しています。
フローの新しい独立した参加者として、アイデンティティプロバイダー (IdP) が登場しました。これは OAuth 2.0 の認可サーバーと同じですが、ユーザーの認証とアイデンティティ管理を担当していることを明示するために IdP という用語を使用しています。
ビジネスが成長すると、同じユーザーデータベースを共有する複数のアプリケーションを持つことになります。OAuth 2.0 と同様に、OpenID Connect を使用すると、1 つの認可サーバーで複数のアプリケーションのユーザーを認証できるようになります。ユーザーが 1 つのアプリケーションに既にサインインしている場合、IdP にリダイレクトされる際に再び資格情報を入力する必要はありません。このフローはユーザーの操作なしに自動的に完了します。これをシングルサインオン(SSO)と呼びます。
ここまでの内容は非常に簡略化されたフローであり、裏にはさらに多くの詳細があります。情報過多を避けるため、一旦次のセクションに進みましょう。
アプリケーションの種類
前のセクションでは、Web アプリケーションを例に挙げましたが、世界はそれ以上に多様です。アイデンティティプロバイダーにとって、使用する正確なプログラミング言語、フレームワーク、プラットフォームは本質的には関係ありません。実際に際立つ違いの一つは、アプリケーションがパブリッククライアントかプライベート(信頼された)クライアントかということです:
- パブリッククライアント:資格情報を秘密に保つことができないクライアントで、リソースオーナー(ユーザー)がアクセスできることを意味します。例えば、ブラウザで動作する Web アプリケーション(例:シングルページアプリケーション)。
- プライベートクライアント:資格情報を秘密に保ち、リソースオーナー(ユーザー)に公開しない能力を持つクライアント。例えば、サーバーで動作する Web アプリケーション(例:サーバーサイド Web アプリケーション)や API サービス。
次に、異なるアプリケーションタイプで OAuth 2.0 と OpenID Connect がどのように使用されるかを見てみましょう。
この投稿の文脈では、「アプリケーション」と「クライアント」は同義です。
サーバー上で動作する Web アプリケーション
アプリケーションはサーバー上で動作し、HTML ページをユーザーに提供します。Express.js、Django、Ruby on Rails などの多くの人気のある Web フレームワークがこのカテゴリ ーに属します。また、バックエンド・フォー・フロントエンド(BFF)フレームワークである Next.js や Nuxt.js も含まれます。これらのアプリケーションには次のような特徴があります:
- サーバーはプライベートアクセスのみを許可するため(一般ユーザーがサーバーのコードや資格情報を見ることはできません)、プライベートクライアントと見なされます。
- 全体的なユーザー認証フローは、「OpenID Connect」セクションで議論したものと同じです。
- アプリケーションは、アイデンティティプロバイダー(すなわち OpenID Connect プロバイダー)によって発行された ID トークンを使用してユーザーを識別し、ユーザー固有のコンテンツを表示することができます。
- アプリケーションを安全に保つために、通常、認可コードフローを使用してユーザー認証を行い、トークンを取得します。
同時に、アプリケーションはマイクロサービスアーキテクチャ内の他の内部 API サービスにアクセスする必要があるかもしれません。または、異なる部分のアクセス制御が必要なモノリシックアプリケーションであるかもしれません。この点については「API を保護する」セクションで議論します。
シングルページアプリケーション (SPAs)
アプリケーションはユーザーのブラウザで動作し、API を介してサーバーと通信します。React、Angular、Vue.js は SPAs の構築に人気のあるフレームワークです。これらのアプリケーションには次のような特徴がありま す:
- アプリケーションのコードが一般に公開されているため、パブリッククライアントと見なされます。
- 全体的なユーザー認証フローは、「OpenID Connect」セクションで議論したものと同じです。
- アプリケーションは、アイデンティティプロバイダー(すなわち OpenID Connect プロバイダー)によって発行された ID トークンを使用してユーザーを識別し、ユーザー固有のコンテンツを表示することができます。
- アプリケーションを安全に保つために、通常、PKCE(Proof Key for Code Exchange)を使用した認可コードフローを使用してユーザー認証を行い、トークンを取得します。
通常、SPAs はデータのフェッチおよび更新のために他の API サービスにアクセスする必要があります。この点については「API を保護する」セクションで議論します。
モバイルアプリケーション
アプリケーションはモバイルデバイス (iOS、Android など) で動作し、API を介してサーバーと通信します。ほとんどの場合、これらのアプリケーションには SPAs と同じ特徴があります。
マシン間 (M2M) アプリケーション
マシン間 アプリケーションは、クライアント としてサーバー(マシン)上で動作し、他のサーバーと通信します。これらのアプリケーションには次のような特徴があります:
- サーバー上で動作する Web アプリケーションのように、M2M アプリケーションはプライベートクライアントです。
- アプリケーションはユーザーを識別する必要がないかもしれません。代わりに、他のサービスにアクセスするために自分自身を認証する必要があります。
- アプリケーションは、アイデンティティプロバイダー(すなわち OAuth 2.0 プロバイダー)によって発行されたアクセストークンを使用して他のサービスにアクセスできます。
- アプリケーションを安全に保つために、通常、クライアント資格情報フローを使用してアクセストークンを取得します。
他のサービスにアクセスする際、アプリケーションはリクエストヘッダーにアクセストークンを提供する必要があるかもしれません。この点については「API を保護する」セクションで議論します。
IoT デバイス上で動作するアプリケーション
アプリケーションは IoT デバイス(例:スマートホームデバイス、ウェアラブルデバイスなど)で動作し、API を介してサーバーと通信します。これらのアプリケーションには次のような特徴があります:
- デバイスの能力によって、パブリッククライアントまたはプライベートクライアントのいずれかになります。
- 全体的な認証フローは、デバイスの能力に応じて「OpenID Connect」セクションで議論したものと異なるかもしれません。例えば、一部のデバイスはユーザーが資格情報を入力するためのスクリーンがないかもしれません。
- デバイスがユーザーを識別する必要がない場合、ID トークンや userinfo エンドポイントを使用する必要がないかもしれません。代わりに、マシン間 (M2M) アプリケーションとして扱うことができます。
- アプリケーションを安全に保つために、PKCE(Proof Key for Code Exchange)を使用した認可コードフローを使用してユーザー認証とトークンの取得を行うか、または クライアント資格情報フローを使用してアクセストークンを取得することができます。
サーバーと通信する際、デバイスはリクエストヘッダーにアクセストークンを提供する必要があるかもしれません。この点については「API を保護する」セクションで議論します。
API を保護する
OpenID Connect を使用することで、ID トークン や userinfo エンドポイント を介してユーザーを識別し、ユーザー固有のデータを取得することが可能です。このプロセスは認証と呼ばれます。しかし、すべての認証されたユーザーにすべてのリソースを公開したいとは思わないことでしょう。たとえば、管理者のみがユーザー管理ページにアクセスできるとします。
ここで認可が関与してきます。OAuth 2.0 は認可フレームワークであり、OpenID Connect は OAuth 2.0 の上に構築されたアイデンティティレイヤーです。つまり、OpenID Connect が既に存在している場合でも、OAuth 2.0 を使用することができます。
「OAuth 2.0」セクションで使用した例を思い出してみましょう:MyApp はユーザーの Google Drive にアクセスしたいです。MyApp がユーザーの Google Drive 内のすべてのファイルにアクセス許可を与えるのは実用的ではありません。代わりに、MyApp は何をアクセスしたいかを明確に指定するべきです(例:特定のフォルダー内のファイルに対する読み取り専用アクセス)。OAuth 2.0 の用語では、これを スコープ と呼びます。
「スコープ」は、OAuth 2.0 の文脈で「許可」と同義に使われる場合があります。時々「スコープ」は技術的ではないユーザーにはあいまいなためです。
ユーザーが MyApp へのアクセスを許可すると、認可サーバーは要求されたスコープと共にアクセストークンを発行します。アクセストークンは、ユーザーのファイルにアクセスするためにリソースサーバー(Google Drive)に送信されます。
自然に、Google Drive を自分の API サービスに置き換えることができます。例えば、MyApp がユーザーの注文履歴を取得するために OrderService にアクセスする必要があるとします。この場合、ユーザー認証は既にアイデンティティプロバイダーによって行われ、MyApp と OrderService はどちらも私たちの管理下にあるため、ユーザーに許可を求める必要はありません;MyApp はアイデンティティプロバイダーによって発行されたアクセストークンと共に直接 OrderService にリクエストを送信できます。
アクセストークンには、ユーザーが注文履歴を読むことができることを示す read:order
スコープ が含まれているかもしれません。
次に、ユーザーが誤ってブラウザに管理者ページの URL を入力したとします。ユーザーが管理者でないため、アクセス トークンには admin
スコープが含まれていません。OrderService はリクエストを拒否し、エラーメッセージを返します。
この場合、OrderService は管理者ページにアクセスする権限がないことを示すために、403 Forbidden ステータスコードを返すかもしれません。
マシン間 (M2M) アプリケーションにおいては、プロセスにユーザーは関与していません。アプリケーションは直接アイデンティティプロバイダーからアクセストークンをリクエストし、それを使用して他のサービスにアクセスできます。同じ概念が適用されます:アクセストークンにはリソースへのアクセスに必要なスコープが含まれています。
認可設計
API サービスを保護するための認可を設計する際に考慮すべき重要な点が 2 つあります:
- スコープ:クライアントがアクセスできる内容を定義します。スコープは要求に応じて細かく(例:
read:order
、write:order
)することも、一般的(例:order
)にすることもできます。 - アクセス制御:特定のスコープを持つことができる人を定義します。例えば、管理者のみが
admin
スコープを持つことができます。
アクセス制御に関して、以下の一般的なアプローチがあります:
- 役割ベースのアクセス制御 (RBAC):ユーザーに役割を割り当て、役割がどのリソースにアクセスできるかを定義します。例えば、管理者の役割は管理者ページにアクセスできます。
- 属性ベースのアクセス制御 (ABAC):属性に基づいてポリシーを定義し、これらの属性に基づいてアクセス制御を行います。例えば、「エンジニアリング」部門のユーザーはエンジニアリングページにアクセスできます。
両方のアプローチにおいて、アクセス制御を検証する標準的な方法は、役割や属性ではなくアクセストークンのスコープを確認することです。役割と属性は非常に動的であり、スコープはより静的で管理が容易です。
アクセス制御の詳細については、RBAC および ABAC: 知っておくべきアクセス制御モデルを参照 してください。
アクセストークン
「アクセストークン」という用語は何度も言及されていますが、取得方法についてはまだ説明していません。OAuth 2.0 では、アクセストークンは成功した認可フローの後に認可サーバー(アイデンティティプロバイダー)によって発行されます。
Google Drive の例を見て、認可コードフローを使用していると仮定してみましょう:
アクセストークン発行の流れにはいくつかの重要なステップがあります:
- ステップ 2 (Google へのリダイレクト): MyApp は認可リクエストと共にユーザーを Google へリダイレクトします。典型的にはこのリクエストには以下の情報が含まれます:
- リクエストを開始しているクライアント(MyApp)
- MyApp が要求しているスコープ
- 認可が終了後、Google がユーザーをリダイレクトする場所
- ステップ 5 (Google Drive へのアクセス許可を求める): Google は、MyApp へのアクセスをユーザーに許可させます。ユーザーはアクセスを許可または拒否できます。このステップを同意と呼びます。
- ステップ 7 (認可データで MyApp へリダイレクト): このステップは新たに導入されました。アクセストークンを直接返す代わりに、Google は MyApp に一度限りの認可コードを返し、より安全な交換を行います。このコードはアクセストークンを取得するために使用されます。
- ステップ 8 (認可コードを使用してアクセストークンを交換): これも新しいステップです。MyApp は Google に対して認可コードを送信し、アクセストークンの交換を行います。アイデンティティプロバイダーとしての Google はリクエストのコンテキストを合成し、アクセストークンを発行するかどうかを決定します:
- クライアント(MyApp)が自身が主張するものであること
- ユーザーがクライアントへのアクセスを許可したこと
- ユーザーが主張する者であること
- ユーザーが必要なスコープを持っていること
- 認可コードが有効で期限が 切れていないこと
上記の例では、認可サーバー(アイデンティティプロバイダー)とリソースサーバーが同じ(Google)であると仮定しています。それらが別々の場合、MyApp と OrderService の例を使ったフローは次のようになります:
このフローでは、認可サーバー(IdP)が MyApp に ID トークンとアクセストークンの両方を発行します(ステップ 8)。ID トークンはユーザーを識別するために使用され、アクセストークンは OrderService のような他のサービスにアクセスするために使用されます。MyApp と OrderService の両方が初めてのサービスであるため、通常、ユーザーにアクセスを許可させることはせず、その代わりにアイデンティティプロバイダーのアクセス制御に依存してユーザーがリソースにアクセスできるかどうかを判断します(つまり、アクセストークンが必要なスコープを含んでいるかどうか)。
最後に、マシン間 (M2M) アプリケーションでアクセストークンがどのように使用されるかを見てみましょう。プロセスにユーザーは関与しないため、信頼されたアプリケーションとして直接アイデンティティプロバイダーからアクセストークンをリクエストできます:
ここでもアクセス制御が適用されることがあります。OrderService にとって、ユーザーが誰であるか、またはどのアプリケーションがデータをリクエストしているかは重要ではなく、アクセストークンとそれが含むスコープだけが重要です。
アクセストークンは通常、JSON Web Tokens (JWT) としてエンコードされています。JWT について詳しく知りたい場合は、What is JSON Web Token (JWT)? を参照してください。
リソースインジケーター
OAuth 2.0 はアクセス制御のためのスコープの概念を導入しています。ただし、あなたはすぐにスコープが十分でないことに気づくかもしれません:
- OpenID Connect は
openid
、offline_access
、profile
などの一連の標準スコープを定義しています。標準スコープとカスタムスコープを混在させるのは混乱を招くかもしれません。 - 同じスコープ名を共有し、異なる意味を持つ複数の API サービスが存在する可能性があります。
一般的な解決策は、スコープ名に接尾辞(または接頭辞)を追加してリソース(API サービス)を示すことです。例えば、read:order
と read:product
は read
と read
よりも明確です。OAuth 2.0 の拡張機能 RFC 8707 は、クライアントがアクセスしたいリソースサーバーを示す新しいパラメータ resource
を導入しています。
現実には、API サービスは通常 URL で定義されているため、リソースインジケーターとして URL を使用する方が自然です。例えば、OrderService の API は次のように表すことができます:
見てのとおり、パラメータは認可リクエストとアクセストークンで実際のリソース URL を使用する利便性をもたらします。RFC 8707 がすべてのアイデンティティプロバイダーによって実装されているわけではないことを言及する価値があります。アイデンティティプロバイダーのドキュメントを確認し、resource
パラメータに対応しているかどうかを確認する必要があります(Logto は対応しています)。
要約すると、resource
パラメータは、クライアントがアクセスしたいリソースを認可リクエストとアクセストークンで示すために使用できます。
リソースインジケーターのアクセス制限はありません。つまり、リソースインジケーターは、API サービスを指す実際の URL である必要はありません。そのため、名前「リソースインジケーター」は認可プロセスにおけるその役割を適切に反映しています。仮想 URL を使用して保護したいリソースを表すことができます。例えば、モノリシックアプリケーションで、管理者のみがアクセスできるリソースを表すために仮想 URL
https://api.example.com/admin
を定義できます。
すべてをまとめる
ここまでで、OAuth 2.0 と OpenID Connect の基本と、それを異なるアプリケーションタイプやシナリオでどのように使用するかをカバーしました。個別に議論しましたが、ビジネス要件に応じてそれらを組み合わせることができます。全体的なアーキテクチャは次のようにシンプルかもしれません:
または少し複雑になるかもしれません:
アプリケーションが成長するにつれて、アイデンティティプロバイダー(IdP)がアーキテクチャで重要な役割を果たすことがわかるでしょう。しかし、それがビジネス目標に直接関係するわけではありません。信頼できるベンダーにオフロードすることは素晴らしい考えですが、アイデンティティプロバイダーを賢く選ぶ必要があります。優れたアイデンティティプロバイダーはプロセスを大幅に簡素化し、開発の労力を軽減し、潜在的なセキュリティの落とし穴から解放してくれます。
終わりの言葉
現代のクラウドアプリケーションでは、アイデンティティプロバイダー(または認可サーバー)がユーザーの認証、アイデンティティ管理、アクセス制御の中心です。この投稿では多くの概念を議論しましたが、そのようなシステムを実装する際には考慮すべき微妙な点がまだ多くあります。もっと詳しく知りたい場合は、当社のブログでより詳細な記事を閲覧することができます。