この記事では『PKCE』について、
- PKCEとは
- PKCEによる認可コード横取り攻撃の対策方法
などを図を用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。
PKCEとは
PKCEとはProof Key for Code Exchangeの略で、「認可コード横取り攻撃」への対策を目的としたOAuth2.0拡張仕様です。
悪意のあるアプリに認可コードが横取りされると、アクセストークンが盗まれてしまう可能性があります。しかし、アプリにPKCEを実装することで、悪意のあるアプリにアクセストークンが盗まれることを防ぐことができます。
あわせて読みたい
『OAuth』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。 続きを見るOAuthとは?『仕組み』などを分かりやすく解説!
補足
- PKCEは2015年9月にRFC7636として定義されています。
- PKCEは「ピクシー」と呼びます。
PKCEによる認可コード横取り攻撃の対策
アプリにPKCEを実装することで、悪意のあるアプリが認可コードを横取りしても、その認可コードを使ってアクセストークンを取得するのは困難になります。
では、以下の場合における認可フローについて順番に見ていきましょう。
- 悪意のあるアプリがない&PKCE未実装の場合の認可フロー
- OAuth2.0の一般的に認可フローです。
- 悪意のあるアプリがある&PKCE未実装の場合の認可フロー
- PKCE未実装によりアクセストークンが盗まれてしまう例
- 悪意のあるアプリがある&PKCE実装済の場合の認可フロー
- PKCEを実装することで、アクセストークンが盗まれるのを防止する例
悪意のあるアプリがない&PKCE未実装の場合の認可フロー
上図に「悪意のあるアプリがない&PKCE未実装の場合の認可フロー」を示しています。
OAuth2.0の認可コードグラントフローでは、ユーザが処理4で認可を行うと、処理5で認可サーバーは設定されたコールバックURLにリダイレクトします。このたコールバックURLには、アクセストークンを取得するための認可コードがパラメータとして含まれています。
スマホアプリの場合、コールバックURLはURLスキームの形式を持つことが多く、アプリ実装者が設定した特定のURLスキームを持つアプリに対してリダイレクトを行います。
補足
スマホアプリでは、URLスキームを利用して特定のアプリを起動することができます。例えば、iOSでブラウザに「music://」と入力すると、Musicアプリが起動するのは、MusicアプリがmusicをURLスキームとして設定しているからです。
悪意のあるアプリがある&PKCE未実装の場合の認可フロー
上図に「悪意のあるアプリがある&PKCE未実装の場合の認可フロー」を示しています。
iOSにおいては、同じURLスキームを持つ複数のアプリをインストールすることが可能です。そのため、アプリ実装者が設定することができるURLスキームではどのアプリを開くまでは特定することができません。例えば、「hogehoge://」というURLスキームでHogeHogeアプリを起動することを期待しても、悪意のあるアプリのURLスキームが同じ「hogehoge://」だったら、悪意のあるアプリが起動する可能性があるのです。
つまり、処理5において、認可サーバーが「hogehoge://」というコールバックURLにリダイレクトした場合、認可サーバーからのリダイレクトを悪意のあるアプリが受け取る可能性が生じます。その結果として、悪意のあるアプリが認可コードを横取りし、アクセストークンが盗まれてしまう可能性があります。
これを防ぐのがPKCE(ピクシー)という仕様になります。
アクセストークンが盗まれてしまう原因は処理2の「認可リクエストしたアプリ」と処理6の「アクセストークンリクエストしたアプリ」が同じかどうかの検証ができていないためです。
PKCEを実装すると、「認可リクエストしたアプリ」と「アクセストークンリクエストしたアプリ」が同じかどうかを検証することができるようになります。
悪意のあるアプリがある&PKCE実装済の場合の認可フロー
上図に「悪意のあるアプリがある&PKCE実装済の場合の認可フロー」を示しています。
PKCEを実装すると、悪意のあるアプリに認可コードが横取りされても、アクセストークンが盗まれるのを防止することができます。
「アプリにPKCEを実装する方法」と「PKCEを実装した場合の認可フロー」を見てみましょう。
PKCEを実装するには、以下の手順を行います。
PKCEを実装する手順
- code_verifierとcode_challengeを生成する
- code_challengeとcode_challenge_methodを認可URLのクエリパラメータに含めて認可サーバーにリダイレクトする
- アクセストークンを要求する際のリクエストボディにcode_verifierを加えて実行する
各手順について順番に説明します。
code_verifierとcode_challengeを生成する
ユーザがアプリにアクセスすると、認可を求められます。この時、アプリ側では「code_verifier」とcode_verifierをSHA256でハッシュ化してBase64URL形式にエンコードした「code_challenge」という2つの文字列を生成します。
「code_verifier」の仕様はRFC7636に記載されています。
code_verifierの仕様
- 文字数:43~128文字
- 使用可能な文字:半角英数字(
a
〜z
、A
~Z
、0
~9
)と記号(-
、.
、_
、~
)からなるランダムな文字列
code_challengeとcode_challenge_methodを認可URLのクエリパラメータに含めて認可サーバーにリダイレクトする
「code_challenge_method」は「code_challenge」の生成方法を表すものです。RFC7636では、「code_challenge」の生成方法として、S256(SHA256を意味する)とplain(暗号化なし)が定義されています。
アプリは「code_challenge」と「code_challenge_method」を認可URLのクエリパラメータに含めて認可サーバーにリダイレクトします。認可サーバー側では「code_challenge」と「code_challenge_method」を紐づけて保存しておきます。
認可URLの例
https://hogehoge/authorization
?response_type=code
&client_id=123456789
&redirect_uri={リダイレクトURLの値}
&code_challenge={code_challengeの値}
&code_challenge_method=S256
アクセストークンを要求する際のリクエストボディにcode_verifierを加えて実行する
アクセストークンを要求する際のリクエストボディに「code_verifier」を加えて実行します。
サンプルコード例
curl -v -X POST https://hogehogeserver/access_token \
-d "grant_type=authorization_code" \
-d "code={認可コードの値}" \
-d "redirect_uri={リダイレクトURLの値}" \
-d "code_verifier={code_verifierの値}"
認可サーバは受け取った「code_verifier」を保存しておいた「code_challenge」と「code_challenge_method」を元に検証します。
詳しく説明すると、保存しておいた「code_challenge_method」が「S256」の場合、受け取った「code_verifier」をSHA256でハッシュ化してBase64URL形式にエンコードしてから「code_challenge」と同じかどうか比較します。
「code_challenge_method」が「plain」の場合、受け取った「code_verifier」が「code_challenge」と同じかどうか直接比較します。
- 比較結果が「同じ」の場合
- 「認可リクエストしたアプリ」と「アクセストークンリクエストしたアプリ」が同じなので、アクセストークンをアプリに発行します。
- 比較結果が「異なる」場合
- 「認可リクエストしたアプリ」と「アクセストークンリクエストしたアプリ」が異なるので、アクセストークンをアプリに発行しません。
- 「invalid_grant」を示すエラー応答を返します。
認可リクエストを行った正規のアプリで「code_verifier」と「code_challenge」を生成しているため、悪意のあるアプリは「code_verifier」の値が分かりません。そのため、「code_verifier」の検証に失敗し、アクセストークンを盗むことができなくなります。
本記事のまとめ
この記事では『PKCE』について、以下の内容を説明しました。
- PKCEとは
- PKCEによる認可コード横取り攻撃の対策方法
お読み頂きありがとうございました。