プロダクト開発

PKCE: OAuthによる認可を安全にする拡張仕様の概要・実装例

OAuthによる認可機能の実装をより安全にするための“PKCE”という仕様の概要と実装例を示します。

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

これを防ぐのがPKCEです。アプリケーションをPKCEに対応することで、第三者によるアクセストークンの取得を防ぐことができます。

著者
ぜに/Hiroki Zenigami

Webエンジニア&プロダクトマネージャ←プログラミング教育で起業←東大院←熊本高専。 共著に「現場で使える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://から始まるコールバックを取得してしまった場合、認可サーバーから第三者のアプリが起動されることになります。これがアクセストークンの流出につながります。

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

PKCEに対応する方法

上述した、悪意のあるアプリへの対策が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対応アプリへの横取りフロー 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に対応した認可フロー 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

Webエンジニア&プロダクトマネージャ←プログラミング教育で起業←東大院←熊本高専。 共著に「現場で使えるRuby on Rails 5」。

関連記事関連書籍人気記事
applis
エンジニアとしてのんびり暮らす
お問い合わせ
ご意見・ご質問やお仕事のご依頼などは下記よりお願いいたします
お問い合わせ
© applis