OpenID Connectのstate・nonce・PKCEについて使用法も含めて説明してみる

なんか適当な値を貼り付けてたら動いたからヨシ!してませんか?
2022.07.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

prismatix事業部の宮部です。

今日はクラスメソッド設立記念日かつ、七夕なので「織姫と彦星が天の川挟んでauth danceやってみた」みたいな記事書こうと思ったんですが自分で何書いてるかわからなくなったのでもう少し実用的な話を考えてみました。

OAuth2.0やOpenID Connectでは各種の攻撃の対策として使用するクエリパラメータが存在します。

ただし、これらのパラメータが付与されているかのハンドリングは簡単ですが、実際にそれをIdentity Provider(認証システム)やRelying Party(利用システム)が正しく検証しているかの保証はむずかしかったりします。

SDKやライブラリにこの辺の処理は任せてるというケースも多いでしょうが、「検証の方法は互いに提供しているのでしっかり検証しているはず」という信頼関係が必要になってくるので啓蒙の意味も込めて各パラメータの用途などについて書いていこうと思います。

 

前提条件

今回は state, nonce, PKCE の説明をするために認可コードフローが前提です。

認可コードフロー

ざっくりとした図になりますが、今回はこの中の6, 8, 10番の検証の部分について説明していきます。

 

state

stateはCSRFを利用して、他者に自身の認可コードを使って処理させるような行為を防ぐことができます。

例えば、4番で取得した認可コード付きのURLを気づかないうちに他者に踏ませてしまえば被害者が個人情報を入力したり、サービスを利用した後で攻撃者が普通にログインしてしまえばその情報を参照することができるでしょう。

この対策として、3番で送る認可リクエストにユーザのセッションに紐づく識別子であるstateを付与すると、4番で認可コードと一緒に先ほど付与したstateが返却されるのでそのstateの値が同一のものかを検証しましょう(6番)

当然、返却されてきたstateを検証しなければ意味がないですし、毎回同じだったり推測可能な値を付与していたとしても同じく悪用されてしまいます。

 

nonce

nonceはなんらかの方法で取得した他者のIdトークンを使って自身の認可コードを使ってRelying Partyで認証しようとするリプレイアタックを防ぐことができます。

Idトークン自体はIdentity Providerが発行した正式なものであるため、Relying Partyはそれを受け入れてしまい、被害者のアカウントとして認証してしまうでしょう。

この対策として、3番で送る認可リクエストにstateと同じくユーザのセッションに紐づく識別子であるnonceを付与すると、9番で受け取ったIdトークンのclaimの中にnonceが存在するのでそのnonceの値が同一のものかを検証しましょう(10番)

こちらもstateと同様に、返却されてきたnonceを検証しなければ意味がないですし、毎回同じだったり推測可能な値を付与していたとしても同じく悪用されてしまいます。

 

PKCE

PKCEはProof Key for Code Exchangeの略称であり、PKCEというパラメータが存在するわけではありません。

PKCEは、認可コードを横取りして別のサービスが利用するのを防ぐことができます。

例えばカスタムURIスキームを使用している場合、悪意のあるサービスがそのカスタムURIスキームを乗っ取っていると4番で認可コードを持ってIdentity Providerからリダイレクトしてきた際に、悪意のあるサービスがその認可コードを使ってIdトークンやアクセストークン、リフレッシュトークンを取得できてしまいます。

この対策であるPKCEでは以下の3つのパラメータが登場します。

  • code_verifier
  • code_challenge_method
  • code_challenge

簡単に説明すると、code_verifiercode_challenge_methodの方式で変換したものがcode_challengeです。

code_challenge_methodにはplainS256の2種類が存在し、plainの場合はcode_verifiercode_challengeの値は同じになり、S256の場合はcode_verifierをSHA-256でハッシュ化したものがcode_challengeの値が同じになります。

まず、3番で送る認可リクエストにcode_challenge_methodcode_challengeを付与します。その後、7番のトークンリクエストで認可コードと一緒にcode_verifierを渡すと、Identity Providerは先ほどの認可リクエスト時に受け取ったcode_challenge_methodで変換したcode_verifiercode_challengeと同じかを検証します(8番)

こちらは、Identity Providerが検証をしなければ意味がないですし、Relying Partyが毎回同じ値や推測可能な値を付与していたとしても悪用されてしまいます。

まとめ

  • stateはcsrf対策
  • nonceはリプレイアタック対策
  • PKCEは認可コードの横取り対策

そして、全部同じ値ではなくちゃんと推測不可能な値を使用し、ちゃんと検証もしましょう。

 

参考資料

 

 

最後に

prismtatixでは、認証・認可・ユーザー管理基盤開発エンジニアを募集しています。

昨今のサービスには必要不可欠な認証認可について一緒に考え、プロダクトを育ててくれる方を探しています。

少しでもご興味をお持ちいただけましたら、ぜひご応募ください。 お待ちしています。