Proof Key for Code Exchange (RFC 7636)

1. イントロダクション

RFC 7636Proof Key for Code Exchange (PKCE, 「ピクシー」と発音します)は、**認可コード横取り攻撃(authorization code interception attack)**に対する対策に関する仕様です。

authorization code interception attack

この仕様は2015年9月にリリースされました。この仕様では、認可コードフローの各種リクエストにいくつかのパラメーターが追加されています。

  1. 認可リクエスト: code_challenge パラメーターと code_challenge_method パラメーターを追加
  2. トークンリクエスト: code_verifier パラメーターを追加

この仕様により、認可サーバーは、code_verifier を持たない悪意のあるアプリからのトークンリクエストを排除することが可能となります。

2. PKCE 認可リクエスト

2.1 リクエストパラメーター

PKCE に対応した認可リクエストには、code_challenge パラメーターをついかします。code_challenge_method パラメーターについては任意です。

2.2 code_challenge の値

code_challenge_method の計算ロジックを code_verifier に適用し、code_challenge の値を計算します。

2.3 code_verifier の値

code_verifier の値は、[A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" からなるランダムな文字列であり、最低43文字、最大128文字の長さが必要となります。

pkce authorization request

2.4 code_challenge_method について

定義されている code_challenge_method の値は、plain および S256 になります。それぞれの計算ロジックは下記のとおりです。

Method Logic
plain code_challenge = code_verifier
S256 code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

plain では、インプットは何ら変換されず、code_verifier の値がそのまま code_challenge の値となります。

S256 では、SHA-256 のハッシュ値を BASE64-URL エンコードした値を用います。例えば、code_verifier の値が dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk である場合、code_challenge の値は E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM となります。

S256 を用いる場合、クライアントはリクエストの中に code_challenge_method=S256 を含める必要があります。 code_challenge_method パラメーターがない場合、認可サーバーは plain が使われていると判断し、処理します。

3. PKCE 認可レスポンス

認可コードを生成した後、認可サーバーはその値と併せて、認可リクエストに含まれているコードチャレンジの値とコードチャレンジメソッドの値をデータベース上に保存します。

認可サーバは保存したこれらの値を、その後クライアントから送られてくるトークンリクエストの検証に用います。

認可エンドポイントから返されるレスポンス自体は、これまでと同様であり、PKCE特有のパラメーター等はありません。

pkce authorization response

4. PCKE トークンリクエスト

認可サーバーから認可コードを受け取った後、クライアントアプリはトークンリクエストを生成します。トークンリクエストの中には、認可コードに加え、コードチャレンジの値から計算したコードベリファイアを埋め込む必要があります。

リクエストパラメーターの中では、コードベリファイアは code_verifier として指定されます。

pkce_token_request

5. PKCE トークンレスポンス

5.1 コードベリファイアの有無を確認する

PKCE をサポートする認可サーバーのトークンエンドポイントは、トークンリクエストの中に正規のコードベリファイアが含まれているのかを確認します。

この確認は、 grant_type が authorization_code であり、トークンリクエスト中の認可コードがコードチャレンジと紐づいている場合に限ります。

これらの条件がそろっていても、トークンリクエスト中にコードベリファイアが含まれていない場合、そのリクエストは悪意のあるアプリからのリクエストと認識し、認可サーバーはエラーを返します。

5.2 コードベリファイアを検証する

検証は、二つのコードチャレンジを比較することで行われます。

コードチャレンジのひとつは、認可リクエストに含まれていたもの(認可サーバーがデータベースに保存しておいたもの)。もう一方は、認可リクエストで指定された方法を用いて、トークンリクエスト中のコードベリファイアから計算される値です。

もしこれら二つのコードチャレンジが同一の場合、そのトークンリクエストは認可リクエストを投げてきた正規のクライアントアプリから送られてきたもの、と認可サーバーは判断します。一致しない場合、認可サーバーは、そのリクエストが悪意のあるクライアントアプリから来たものと判定します。

5.3 トークンを発行する

トークンリクエストが正規の場合、認可サーバーは通常通りトークンを発行します。

pkce_token_response

6. PKCE を試す

6.1 準備する

6.1.1 サインアップする

Authlete のアカウントをお持ちでない場合は、まず初めにサインアップしてください。

6.1.2 サービス API キーとクライアント ID

認可リクエストをするためには、サービス API キーと クライアント ID が必要となります。

サインアップと同時に、自動的に認可サーバーとクライアントアプリが一つずつ生成されるため、すでにサービス API キーとクライアント ID は発行されていています。

それぞれ、アカウント登録時に送信されるメールに記載されていますし、管理者コンソールおよびクライアントコンソールから確認することもできます。

6.1.3 サービスとクライアントの設定

認可サーバー(管理者コンソールサービス)及びクライアントアプリ(クライアントコンソールアプリ)の設定は下記を参考にしてください。

Table. Settings of Service in Service Owner Console

カテゴリ パラメーター
認可 サポートする認可種別 少なくとも AUTHORIZATION_CODE にチェックを入れる
認可 サポートする応答種別 少なくとも CODE にチェック入れる
認可 ダイレクト認可エンドポイントの有効化 有効
認可 ダイレクトトークンエンドポイントの有効化 有効

Table. Settings of Client in Client Developer Console

カテゴリ パラメーター
基本情報 クライアントタイプ PUBLIC を選択する
認可 認可種別 少なくとも AUTHORIZATION_CODE にチェックを入れる
認可 応答種別 少なくとも CODE にチェック入れる
認可 リダイレクト URI https://api.authlete.com/api/mock/redirection/service-api-key

6.2. 認可レスポンス

6.2.1. 認可エンドポイントにアクセスする

下記の URL にブラウザからアクセスしてください。その際、service-api-keyclient-id は自身のものに置き換えてください。

https://api.authlete.com/api/auth/authorization/direct/service-api-key
  ?client_id=client-id
  &response_type=code
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

上記にある code_challengecode_challenge_method は、これまで説明してきたPKCE関連のパラメーターになります。

6.2.2 認可リクエスト

認可エンドポイントにおいて、認可ページが表示されます。その中にあるログインフォームに、サービスの API キーと API シークレットを入力し、認可ボタンをクリックしてください。

login form in authorization page

ここで、あたかもエンドユーザーのログイン ID とパスワードののように、サービスの API キーとシークレットを入力していますが、その技術的な背景については、クイックガイドの"3.2. Input Credentials“にあるテクニカルノートをご参照ください。

6.2.3 認可レスポンス

認可エンドポイントから返されるレスポンスは、事前に登録されたクライアントのリダイレクトエンドポイントに送られます。アカウント登録時点でリダイレクト URI を登録済みのため、認可エンドポイントからのレスポンスは、https://api.authlete.com/api/mock/redirection/service-api-key に返されます。

このリダイレクトエンドポイントは、開発用に Authlete が用意した実装です。この実装では、認可レスポンスとして受け取った各種パラメーターの値が表示されます。例えば、認可レスポンスが認可コードを含んでいれば、リダイレクトエンドポイントでは下記のように表示されます。

authorization_code

加えて、認可エンドポイントから ID トークンを発行する場合、ID トークンの内容を表示します。

また、トークンリクエストを行うフローの場合、トークンリクエストを送信するためのフォームが表示されます。

6.3 トークンリクエスト

6.3.1 トークンリクエストフォーム

直前のセクションでも述べましたが、認可レスポンス中に認可コードが含まれている場合、トークンリクエストを送信するためのフォームが表示されます。

そのフォームには、コードベリファイアを入力する欄があります。ここでは、dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk と入力して下さい。

token_request_form

コードベリファイア付きのトークンリクエストを送信するために、「送信」ボタンをクリックしてください。なお、認可コードは10分で失効するように設定するため、10分以内にボタンをクリックしてください。

6.3.2. トークンレスポンス

認可コードと入力したコードベリファイアが正しい場合、JSON 形式でアクセストークンが返送されます。

{
    "access_token": "KPLXrl_wJSHqU708R9kp3bNRGi0LgKUdh0kh-CQhx9g",
    "refresh_token": "YLRJXfratV4yq0_65seCT0bF6YxxgU5jKBUvhOZPrb4",
    "scope": null,
    "token_type": "Bearer",
    "expires_in": 86400
}

おめでとうございます!これで、PKCE に対応した認可コードフローでアクセストークンの発行が完了しました。

6.4. PKCE Configuration

Authlete は、PKCE のための設定項目を一つ用意しています。

管理者コンソール にログインし、対象となるサービスを選択してください。認可タブの中に、『コード交換用証明キー (RFC 7636)』という項目が存在します。

pkce_configuration

このパラメーターを「要求する」とすると、認可コードフローの認可リクエストにおいて、code_challenge パラメーターが常に必須となり、認可コードを含まない認可リクエストはすべてリジェクトされます。デフォルトの値は「要求しない」となっていますが、「要求する」とした方がよりセキュアになります。