はじめに
最近OpenID Connect(OIDC)を使用したログインを実装するにあたって、implecit flowを用いる方法があることを教えてもらったので調査した結果をまとめてみます。
関連するRFCや信頼できそうな記事、理解しやすい記事を含めたいと思います。
参考: OIDCのImplicit FlowでClientSecretを使わずにID連携する
結論
- OAuth2.0のimplicit flowはセキュリティリスクがある
- OIDCのimplecit flowはOAuth2.0のimplicit flowと比較するとセキュリティリスクが少ない
- ソーシャルログインを実現する場合、要件によってはOIDCのimplecit flowを使用することでクライアントシークレットの管理なしで実現できる
- その場合はアクセストークンを発行してリソース取得エンドポイントを叩くのではなく、IDTokenに含まれる情報を使用する
OIDCとは
現代のウェブアプリケーションでは、ユーザーが他のサービスを通じて自身を認証する機能が実装されていることがあり、アカウント管理の煩雑さを解消し、開発者にとってはセキュリティのリスクや認証機能の実装の手間の削減を実現できます。このプロセスは「ソーシャルログイン」としてよく知られています。
OIDCはOAuth2.0という認可プロトコルを基盤とした認証の仕組みで、クライアントがエンドユーザーの同意を通じて、エンドユーザーの情報を安全に取得するための仕組みです。
OIDCのフロー
OIDCのフローは大きく以下のステップで構成されます。
-
認証認可リクエスト: クライアントはユーザーをOpenIDプロバイダー(例えばGoogle)の認可サーバーにリダイレクトします。この時、クライアントはリクエストにスコープopenidを含めます。
-
ユーザー認証: ユーザーはOpenIDプロバイダーで自身のアイデンティティを認証します。成功すると、ユーザーはクライアントにリダイレクトされます。
-
認可レスポンス: リダイレクト時に、認可サーバーはcodeという認可コードをクライアントに渡します。
-
トークンリクエスト: クライアントはこの認可コードを使い、認可サーバーにアクセストークンとIDトークンを要求します。
-
トークンレスポンス: 認可サーバーはアクセストークンとIDトークンをクライアントに返します。
RFC6749に定義されているOAuth2.0の認可コードフローと同じフローになります。
このフローは、response_type=code
を指定した場合に実行されるフローとなります。
ただし、scope
にopenid
を含めた場合のみIDトークンが発行されます。
OIDCはIDトークンを発行するためのフローともいえると認識しているので基本的にはscope
にopenid
を含めることになると思います。
implecit flowについて
OAuth2.0にもimplecit flowが存在し、セキュリティ的なリスクがあって推奨されていないことは知っていたのですが、OIDCにもimplecit flowがあることを知らず、明確に違いを認識できていませんでした。 まずはOAuth2.0のimplecit flowについての復習から始めます。
OAuth2.0のimplecit flow
認可コードフローとは違い、認可エンドポイントにリクエストを投げ、応答として直接アクセストークンを受け取るフローです。
元々はJavaScriptを用いたブラウザベースのクライアント向けに設計されました。
フローの流れとしては以下のようになります。
- ユーザーがクライアントを認可すると、クライアントはリダイレクトURIにアクセストークンを含むリダイレクトレスポンスを受け取ります。
- クライアントはリダイレクトレスポンスからアクセストークンを抽出し、そのトークンを使用してリソースサーバーからリソースを取得します。
参考: OAuth 2.0 Implicit Grant Flow
ただ、このフローは設計上の脆弱性を持っています。 この脆弱性については以下の記事が詳しいです。
参考: OAuth 2.0 Implicit Flowをユーザー認証に利用する際のリスクと対策方法について #idcon
参考: 「単なるOAUTH 2.0を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる」について
なお、RFCにおいても現在のセキュリティベストプラクティスをまとめた文書が発表されていて、原則認可コードフローの利用を推奨しています。また、PKCEを組み合わせて使用することが推奨されています。
OIDCのimplecit flow
OpenID Connectのimplecit flowはOAuth2.0のimplecit flowをベースにしています。そのため、同様のセキュリティリスクを抱えています。
ただし、OIDCのimplecit flowはOAuth2.0のimplecit flowとは異なり、IDトークンを発行するフローであり、このIDトークンにデジタル署名が含まれているため、クライアントはトークンが信頼できる発行者から発行され、改竄されていないことを確認できます。
ただし、OAuth 2.0のimplecit flowと同様に、IDトークンが直接ブラウザに渡されるため、トークンがブラウザの履歴やログ、HTTPリファラに記録される可能性は残っていますが、response_type=id_tokenを指定した場合はアクセストークンではなくID Tokenのみが記録されるため、OAuth 2.0のimplecit flowの問題に該当しないと思っています。
ただし、OIDCのセキュリティの利点を活用するためにはIDトークンの署名を検証することが大切です。
この署名の検討がOAuth2.0と大きく異なるセキュリティにおける重要なポイントです。
IDトークンについて
参考: IDトークンが分かれば OpenID Connect が分かる
参考: OpenID Connect Core 1.0 incorporating errata set 1
IDトークンはJWT形式で発行されます。このJWTには以下のようなクレーム(名前と値のペア)が含まれます。
クレーム | 説明 |
---|---|
iss | トークンの発行者。これはトークンが誰から発行されたかを識別するためのもの。 |
sub | 主題(Subject)。これはトークンが誰についてのものであるかを識別するためのもので、通常はユーザーの一意の識別子。 |
aud | オーディエンス(Audience)。トークンの受け取り手を指定します。トークンはこのオーディエンスに対してのみ有効。 |
exp | 有効期限(Expiration)。トークンの有効期限(UNIX時間) |
iat | 発行時刻(Issued At)。トークンが発行時刻(UNIX時間) |
auth_time | 認証時刻(Auth Time)。ユーザーが最後に認証された時刻(UNIX時間) |
nonce | リプレイ攻撃を防止するための文字列。リクエスト時にクライアントが送信し、IDトークンの発行時にそのまま返される。 |
acr | 認証コンテクストクラス参照(Authentication Context Class Reference)。ユーザーの認証がどのレベルで行われたかを示すもの。 |
amr | 認証方法参照(Authentication Methods References)。ユーザー認証に使用されたメソッド |
azp | 承認済みパーティ(Authorized party)。トークンが発行されたクライアント |
これらはあくまでOIDCで定義された標準的なクレームなので、IDトークンはこれらに加えてカスタムクレームを持つこともできます。
ソーシャルログインの実装方針
今回の記事の目的であるクライアントシークレットの管理なしでログイン機能を実施するには、先述したIDトークンに含まれる情報を使用します。
IDトークンに含まれる情報は、ユーザーの識別子やメールアドレスなどの情報が含まれているため、これを使用してユーザーを識別することができます。
ログインのみの実装であれば発行者、ユーザーの識別子、クライアントの情報があれば実装できるため、クライアントシークレットの管理なしで実装できます。
まとめ
- OAuth2.0のimplicit flowはセキュリティリスクがある
- OIDCのimplecit flowはOAuth2.0のimplicit flowと比較するとアクセストークンではなくIDトークンが発行され、トークン自体の検証ができ、セキュリティリスクが少ない
- ソーシャルログインを実現する場合、要件によってはOIDCのimplecit flowを使用することでクライアントシークレットの管理なしで実現できる
- その場合はアクセストークンを発行してリソース取得エンドポイントを叩くのではなく、IDTokenに含まれる情報を使用する
所感
以前記事にしたりしてわかった気になっていた認証認可周りについて改めて調査しなおすいい機会になりました。 ただ闇雲に仕様を追うことだけでなく、要件を意識することの大切さを改めて感じた次第です。 こうした調査を通して、自分の知識の不足を感じることが多々ありますが、できる限り信頼のおける情報元を探し、こうしてアウトプットしていくことをこれからも継続していきたいと思います!