OAuth 2.0 と OpenID Connect でクラウドベースのアプリケーションを保護する
OAuth 2.0 と OpenID Connect を使用してクラウドアプリケーションのセキュリティを確保し、認証と認可で優れたユーザー体験を提供するための完全ガイド。
OAuth 2.0 と OpenID Connect を使用してクラウドアプリケーションのセキュリティを確保し、認証と認可で優れたユーザー体験を提供するための完全ガイド。
クラウドベースのアプリケーションは、現在のトレンドです。アプリケーションの種類はさまざまですが(ウェブ、モバイル、デスクトップなど)、いずれもストレージ、コンピューティング、データベースといったサービスを提供するクラウドバックエンドを持っています。これらのアプリケーションは、多くの場合、ユーザーを認証して特定のリソースにアクセスする権限を与える必要があります。
自家製の認証と認可メカニズムを作成することも可能ですが、クラウドアプリケーションの開発が進むにつれて、セキュリティが最も重要な懸念事項の1つとなっています。幸いにも、私たちの業界には、OAuth 2.0 や OpenID Connect のような実績のある標準があり、安全な認証と認可の実装を助けてくれます。
この投稿では、以下の前提があります:
はい、「聞いたことがある」で十分です。この投稿では、実際の例を使用して概念を説明し、図を使ってプロセスを説明します。それでは始めましょう!
OAuth 2.0 と OpenID Connect に精通している場合でも、このセクションで実際の例をいくつか紹介するので、引き続きお読みいただけます。この標準に不慣れな方も、簡単に紹介するので安全です。
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 に進みましょう。
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 アプリケーションを例に挙げましたが、世界はそれ以上に多様です。アイデンティティプロバイダーにとって、使用する正確なプログラミング言語、フレームワーク、プラットフォームは本質的には関係ありません。実際に際立つ違いの一つは、アプリケーションがパブリッククライアントかプライベート(信頼された)クライアントかということです:
次に、異なるアプリケーションタイプで OAuth 2.0 と OpenID Connect がどのように使用されるかを見てみましょう。
この投稿の文脈では、「アプリケーション」と「クライアント」は同義です。
アプリケーションはサーバー上で動作し、HTML ページをユーザーに提供します。Express.js、Django、Ruby on Rails などの多くの人気のある Web フレームワークがこのカテゴリーに属します。また、バックエンド・フォー・フロントエンド(BFF)フレームワークである Next.js や Nuxt.js も含まれます。これらのアプリケーションには次のような特徴があります:
同時に、アプリケーションはマイクロサービスアーキテクチャ内の他の内部 API サービスにアクセスする必要があるかもしれません。または、異なる部分のアクセス制御が必要なモノリシックアプリケーションであるかもしれません。この点については「API を保護する」セクションで議論します。
アプリケーションはユーザーのブラウザで動作し、API を介してサーバーと通信します。React、Angular、Vue.js は SPAs の構築に人気のあるフレームワークです。これらのアプリケーションには次のような特徴があります:
通常、SPAs はデータのフェッチおよび更新のために他の API サービスにアクセスする必要があります。この点については「API を保護する」セクションで議論します。
アプリケーションはモバイルデバイス (iOS、Android など) で動作し、API を介してサーバーと通信します。ほとんどの場合、これらのアプリケーションには SPAs と同じ特徴があります。
マシン間 アプリケーションは、クライアント としてサーバー(マシン)上で動作し、他のサーバーと通信します。これらのアプリケーションには次のような特徴があります:
他のサービスにアクセスする際、アプリケーションはリクエストヘッダーにアクセストークンを提供する必要があるかもしれません。この点については「API を保護する」セクションで議論します。
アプリケーションは IoT デバイス(例:スマートホームデバイス、ウェアラブルデバイスなど)で動作し、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: 知っておくべきアクセス制御モデルを参照してください。
「アクセストークン」という用語は何度も言及されていますが、取得方法についてはまだ説明していません。OAuth 2.0 では、アクセストークンは成功した認可フローの後に認可サーバー(アイデンティティプロバイダー)によって発行されます。
Google Drive の例を見て、認可コードフローを使用していると仮定してみましょう:
アクセストークン発行の流れにはいくつかの重要なステップがあります:
上記の例では、認可サーバー(アイデンティティプロバイダー)とリソースサーバーが同じ(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、offline_access、profile などの一連の標準スコープを定義しています。標準スコープとカスタムスコープを混在させるのは混乱を招くかもしれません。一般的な解決策は、スコープ名に接尾辞(または接頭辞)を追加してリソース(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)がアーキテクチャで重要な役割を果たすことがわかるでしょう。しかし、それがビジネス目標に直接関係するわけではありません。信頼できるベンダーにオフロードすることは素晴らしい考えですが、アイデンティティプロバイダーを賢く選ぶ必要があります。優れたアイデンティティプロバイダーはプロセスを大幅に簡素化し、開発の労力を軽減し、潜在的なセキュリティの落とし穴から解放してくれます。
現代のクラウドアプリケーションでは、アイデンティティプロバイダー(または認可サーバー)がユーザーの認証、アイデンティティ管理、アクセス制御の中心です。この投稿では多くの概念を議論しましたが、そのようなシステムを実装する際には考慮すべき微妙な点がまだ多くあります。もっと詳しく知りたい場合は、当社のブログでより詳細な記事を閲覧することができます。