1
1

More than 3 years have passed since last update.

PKCEのCode_VerifierとCode_ChallengeをJMeterリクエストに組み込む方法

Last updated at Posted at 2020-06-07

はじめに

OAuthでの認可コード横取り攻撃対策としてPKCE(RFC 7636)という仕様が策定され、クライアント側でCode_VerifierとCode_Challengeと呼ばれるコードを生成し、リクエストのパラメータに埋め込む仕様になっている。

PKCE対応したAPIサービス開発でJMeterを利用した負荷試験を実施することになり、クライアント側のコード生成処理をJMeterに組み込むことなったため、その時に組み込んだ方法や手順について解説する。

Code_Verifier、Code_Challengeの生成

JMeterでリクエストに埋め込むコード(Code_Verifier、Code_Challenge)を生成するため、コード生成するクラスを含むjarを生成する。
コード生成アルゴリズムはRFC 7636にて下記の通り定義されているので、仕様に従ってコード生成を行うJavaプログラムを実装する。

code_verifier = high-entropy cryptographic random STRING using the
unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
from Section 2.3 of [RFC3986], with a minimum length of 43 characters
and a maximum length of 128 characters.

省略

code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

省略

ABNF for "code_challenge" is as follows.

code-challenge = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

Code_Challengeの生成でSHA256Hash化アルゴリズムを利用するため、commons-codecを、Base64エンコーディングのためにcommons-lang3を利用する。

package com.sample.pkce;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils;
import java.util.Base64;

public class PKCECode {

    private final String CODE_VERFIER_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~";

    public String getCodeVerifier(final int codeVerifierLength) {
        return RandomStringUtils.random(codeVerifierLength, CODE_VERFIER_STRING);
    }

    public String getCodeChallenge(final String codeVerifier) {
        final byte[] hashedCodeVerifier = DigestUtils.sha256(codeVerifier);
        final String base64EncodedCodeVerifier = Base64.getEncoder().encodeToString(hashedCodeVerifier);
        final String base64URLEncodedCodeVerifier = base64EncodedCodeVerifier
                .replaceAll("=","")
                .replaceAll("\\+","-")
                .replaceAll("/","_");
        return base64URLEncodedCodeVerifier;
    }
}

実装したプログラムが正しくCode_Challengeを生成できているか確認のため、RFC 7636のAppendixの変換例をと同じCodeVerifierを利用してテストを行い、問題なく変換できていることが確認できた。

@Test
public void getCodeChallengeTest() {
    final String codeVerifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
    final PKCECode pkceCode = new PKCECode();
    Assert.assertEquals("E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", pkceCode.getCodeChallenge(codeVerifier));
}

JMeterでリクエスト

JMeterを起動する前に、利用したライブラリのcommons-codeccommons-lang3および生成されたjarファイルをJMeterの外部libフォルダ(<JMeterインストール先>\lib\ext)に格納する。

その後、JMeterを起動し、テスト計画に「追加」「前処理」からBeanShell PreProcessorを選択し下記スクリプトを入力する。

import com.sample.pkce.PKCECode 

PKCECode pkceCode = new PKCECode();
String codeVerifier = pkceCode.getCodeVerifier(43); //Code_Verifierの文字長を指定する
String codeChallenge= pkceCode.getCodeChallenge(codeVerifier);

vars.put("codeVerifier",codeVerifier );
vars.put("codeChallenge",codeChallenge);

その後、HTTPリクエストのリクエストパラメータ等に${codeVerifier}${codeChallenge}でスクリプトで埋め込んだ値をリクエスト設定します。

補足

今回はBeanShell PreProcessorでコードの生成処理を組み込む方法について解説したが、毎回コード生成を行うとクライアントに処理負荷がかかり、思うように負荷をかけることができない場合がある。そのため、性能負荷試験を行う場合は下記2通りのいずれかを選択することを推奨する。

  • 事前に大量のコードをCSVファイルに出力して、JMeterの``で読み込ませる方法
  • 全リクエストでCode_Verifier、Code_Challengeを固定値として、JMeterのパラメータに直接設定する
    • 固定値でよいのであれば、わざわざ新しくコード生成せずにRFC 7636のAppendixの変換例を利用する方法もある

参考

なぜ、43文字~128文字なのかなどの解説についてはこちら詳しく解説していました。参考にどうぞ。

1
1
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
1
1