LoginSignup
7
3

More than 1 year has passed since last update.

PKCE実装で使うcode_verifierとcode_challengeをPHPで実装する。

Last updated at Posted at 2021-12-12

こちらはエキサイトホールディングス Advent Calendar 2021の13日目の記事です。今回はPKCE実装で使用するcode_verifierとcode_challengeをPHPを使って実装してみたいと思います。

PKCE(ピクシー)とは?

PKCE(Proof Key for Code Exchange by OAuth Public Clients)とはRFC7636で定義されている、認可コード横取り攻撃の対策として提案された仕様です。 認可コード横取り攻撃とは、認可コードを不正に取得することによって、アクセストークンなどのトークンを不正に取得する攻撃です。

PKCEに関しては、下記2つの記事がわかりやすかったです。
OAuth2.0拡張仕様のPKCE実装紹介 〜 Yahoo! ID連携に導入しました - Yahoo! JAPAN Tech Blog
LINEログインをPKCE対応する | LINE Developers

今回は、PKCEに必要なcode_verifierとcode_challengeをPHPを使って実装します。

code_verifierの生成

code_verifierの仕様は、RFC7636 に準拠して作成します。

NOTE: The code verifier SHOULD have enough entropy to make it
impractical to guess the value. It is RECOMMENDED that the output of
a suitable random number generator be used to create a 32-octet
sequence. The octet sequence is then base64url-encoded to produce a
43-octet URL safe string to use as the code verifier.

  • 推測困難な乱数値を使うために、適当な乱数生成器を用いる。
  • 十分なエントロピー(256ビット以上)を持たせるために32オクテットの値を利用。
  • 構成文字: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
  • 文字数:43文字〜128文字

上記仕様を満たすように実装をしていきます。

乱数ジェネレーターを使用して32オクテットの値を生成

十分なエントロピーを持っている値を生成します。
適切な乱数ジェネレーターを使用して32オクテットの値を生成します。(1オクテット = 8ビット)

PHPの openssl_random_pseudo_bytes(); は、疑似ランダムなバイト文字列を生成します。

openssl_random_pseudo_bytes(32)
=> string(32) "Gs�<NKD�3[mm�X�t��=7Hm�"!kH"

ここで、「1バイトが必ずしも8ビットとは限らないのでは?」と思った方もいるかもしれません。

openssl_random_pseudo_bytes(); はC言語で実装されています。
C言語では、<limits.h>ヘッダで定義されるCHAR_BITマクロでchar型のビット数を定義しています。
CHAR_BITマクロは少なくとも1バイトが8ビット以上に定義されることが保証されているので、1バイト文字列が8ビット以上であると考えて良さそうです。

Base64でエンコードを行う。

base64は、A-Z(26)、a-z(26)、0-9(10)、+, /(2)、パティングを表す=を使って表現されます。

base64_encode(openssl_random_pseudo_bytes(32)
=> string(44) "euG+/gkfxtLrZakr5GqZojfSBB4t9lLKogDNW4/gauM="

使用可能な構成文字に対応をする。

URL safeにする必要があるので、+, /を置き換えて、=を削除します。

str_replace('=', '', strtr(base64_encode(openssl_random_pseudo_bytes(32)), '+/', '-_'));
=> string(43) "jIhzUc6JtFe1OQSg4v6-VbJuqhVZBalkq0FEEzwYoxs"

code_verifierを生成する関数

code_verifierを生成する関数の完成です。

private function generateCodeVerifier(int $byteLength = 32){
    return str_replace('=', '', strtr(base64_encode(openssl_random_pseudo_bytes($byte_length)), '+/', '-_'));
}

読みやすさを意識して、説明変数を使うと下記のようになります。

private function generateCodeVerifier(int $byteLength = 32)
{
    $randomBytesString = openssl_random_pseudo_bytes($byteLength);
    $encodedRandomString = base64_encode($randomBytesString);
    $urlSafeEncoding = [
        '=' => '',
        '+' => '-',
        '/' => '_',
    ];
    return strtr($encodedRandomString, $urlSafeEncoding);
}   

code_challengeの生成

code_verifierをSHA256で暗号化し、Base64URL形式にエンコードすることでcode_challengeを生成できます。
また、「=」の削除、+を-に、/を_に置換します。

code_challengeを生成する関数

    private function generateCodeChallenge($code_verifier)
    {
        $hash = hash('sha256', $code_verifier, true);
        return str_replace('=', '', strtr(base64_encode($hash), '+/', '-_'));
    }

まとめ

PKCE実装のPHPでの実装例があまりなかったので、作成してみました。
より良い書き方や改善点があればコメントいただけると幸いです。

弊社のアドベントカレンダー他記事もよろしくお願いしますー!
https://qiita.com/advent-calendar/2021/excite-hd

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3