目次

PKCEとは?OAuth 2.0による認可を安全にする拡張仕様を解説します

本サイトはアフィリエイトプログラムを利用した広告を表示しています

ユーザーの認可にはOAuthを用いるのが一般的です。OAuthではアクセストークンをやりとりしますが、OAuth標準の仕様だと、悪意のある第三者にアクセストークンを取得されるおそれがあります。

これを防ぐのがPKCEです。アプリケーションをPKCEに対応することで、第三者によるアクセストークンの取得を防ぐことができます。この記事では、OAuthによる認可機能の実装をより安全にするためのPKCEという仕様の概要と実装例を示します。

著者
Hiroki Zenigami

テクニカルライター。元エンジニア。共著で「現場で使えるRuby on Rails 5」を書きました。プログラミング教室を作るのが目標です。

スポンサーリンク

PKCEとは何か

PKCEは「アクセストークンを安全にやり取りするための、OAuth 2.0の拡張仕様」です。Proof Key for Code Exchangeの略で、意訳すると「安全にコードをやりとりするための証明鍵」となります。

PKCEは2015年9月にRFC7636として定義されています。「ピクシー」と発音します。

一般的な認可フロー

OAuthを用いた一般的な認可のフローを見てみます。ユーザーがアプリにアクセスすると、認可を求められます。ユーザーは認可サーバー上で認証を行い、成功すると認可サーバーからアプリ側にコードが渡ります。

一般的な認可のフロー

アプリは受け取ったコードを元に、認可サーバーからアクセストークンを取得します。あとはアクセストークンを元にプロフィール情報などを取得して、ユーザーに画面を表示します。これが一般的な認可のフローになります。

第三者によるアクセストークンの横取りフロー

一般的な認可のフローでは、手順5でアプリがコールバックを受け取っています。このコールバックを、「悪意のあるアプリ」に取得されてしまった場合のフローを見てみます。

ユーザーが認可サーバー上で認証を行うまでは同じですが、認証後にアプリではなく第三者にコールバックが渡ってしまいます。

第三者による横取りフロー

このコールバックには、アクセストークンを取得するためのコードが含まれています。つまり、第三者がアクセストークンを取得できてしまいます。アクセストークンがあれば、ユーザーの個人情報を取得したり、機能を操作することができてしまいます。これを防ぐのがPKCEという仕様になります。

スポンサーリンク

「悪意のあるアプリ」とは

上のフロー図で「悪意のあるアプリ」というのが出てきました。この第三者は、どうやってコールバックを取得するのでしょうか。

具体的にはカスタムURLスキームを利用する方法があります。例えばiPhoneでは、ブラウザにmusic://というURLを入力するとMusicアプリを起動できます。このようにURLの形式で任意のアプリを開くのがURLスキームです。

ただ、開発者が設定できるカスタムURLスキームでは、どのアプリを開くかまでは特定できません。つまり、あなたのアプリがfoo://というURLスキームでの起動を想定しても、実際には第三者のアプリが起動する可能性が出てきます。

第三者があなたのアプリのfoo://から始まるコールバックを取得してしまった場合、認可サーバーから第三者のアプリが起動されることになります。これがアクセストークンの流出につながります。

URLスキームが非推奨とされている例

この問題を防ぐために、例えばLINEはURLスキームでのアプリ起動を非推奨にしています。アプリを開発する上では、このような乗っ取りの可能性があることを想定しておきたいです。

PKCEに対応する方法

上述した、悪意のあるアプリへの対策がPKCEです。PKCEに対応した場合の認可のフローを見てみます。

PKCEに対応した認可フロー

ユーザーがアプリにアクセスすると認可を求められます。このとき、アプリ側ではcode_verifiercode_challengeという二つの文字列を生成します。

文字列概要
code_verifierリクエストを検証するための鍵ns2KCK1Ixjxo1XXl...
code_challengecode_verifierを暗号化したもの7jvJI4-Gzlc69T0x...

アプリはcode_challengeを認可用URLに含めることで認可サーバーに送ります。認可サーバー側で、このcode_challengeを保管しておきます。あとは、アクセストークンをリクエストするときにcode_verifierを付け加えます。認可サーバーはcode_verifierが正しいかどうかを、保管しておいたcode_challengeを元に検証します。

code_verifierが正しければアクセストークンを返却し、間違っていれば何も返しません。code_verifierは正規のアプリしか知ることはないので、第三者にアクセストークンが横取りされることがなくなる、という仕組みです。

第三者による横取りの例

PKCEに対応したアプリに対して、悪意のあるアプリが横取りを試みるフローを見てみます。第三者がコールバックURLを取得できたとします。この場合でも、第三者がcode_verifierを知ることができません。

PKCE対応アプリへの横取りフロー

認可サーバーはリクエストのcode_verifierを元に検証を試みますが、正しい値ではないため検証に失敗します。つまり、アクセストークンが返されることはありません。

スポンサーリンク

PKCEのアプリ側の実装例

次に、PKCEに対応するための具体的な実装例を見てみます。ここでは参考実装として、Rubyのコードを示します。PKCE対応において重要なのはcode_verifiercode_challengeの生成です。この二つの仕様は次の通りです。

文字列仕様
code_verifier43〜128文字。半角英数、-._~の記号で構成される。推測されてはならず、適切な乱数生成器を用いて生成する
code_challengecode_verifierをSHA256で暗号化し、Base64URL形式にエンコードしたもの

実装例は次の通りです。

# code_verifier
code_verifier = SecureRandom.alphanumeric(64)

# code_challenge
code_challenge = Base64.urlsafe_encode64(OpenSSL::Digest::SHA256.digest(code_verifier), padding: false)

この二つの文字列を説明するために、以下にPKCE対応のフロー図を再掲します。

PKCEに対応した認可フロー

ここでいう認可用URLのパラメータとしてcode_challengeを付与します。また、あわせてcode_challenge_methodという値も含めます。code_challenge_methodは「code_verifierをどんな方法で暗号化したか」で、SHA256を表すS256という文字列で固定します。

https://xxx/authorize?xxx=xxx&code_challenge=abcdefg...&code_challenge_method=S256

あとは、アクセストークンのリクエスト時にcode_verifierを含めるだけです。

$ curl -v -X POST https://xxx/access_token \
       -d 'xxx=xxx' \
       -d 'code_verifier=123456...'

こうすることで、認可サーバー側でcode_verifierを検証してくれます。アプリ側の実装としては、やることは以上になります。これだけで、第三者によるアクセストークンの横取りを防ぐことができます。

PKCEの対応状況

PKCEの仕様は2015年9月に策定され、各社が対応しています。例えば認証サービスのAuth0では、モバイルアプリでのPKCE対応を必須、SPAでも推奨としています。

Yahoo! IDでもPKCEへの対応が可能になっており、またLINEの認証サービス「LINEログイン」でもPKCE対応が推奨されています。Rubyの認可ライブラリ「OmniAuth」では、オプションでPKCEを有効化できます。

インターネットの標準化団体であるIETFのOAuth 2.0 Security Best Current PracticeでもPKCE対応についての記述があるように、PKCEに対応することでより安全なアプリをユーザーに提供することができます。

あわせて読みたい

著者
Hiroki Zenigami

テクニカルライター。元エンジニア。共著で「現場で使えるRuby on Rails 5」を書きました。プログラミング教室を作るのが目標です。

スポンサーリンク

ブログをはじめよう

技術ブログの始め方を、たくさんの画像で分かりやすく解説しました。これまでブログをやったことがない人でも、エンジニアにとって重要なブログを今日から始められます。

ブログをはじめる