サーバーエンジニアの原 @zetta1985 です。
認証済みで使用されるWeb APIの多くは、リクエストURLやパラメータをベースに生成したHMACをHTTPリクエストヘッダーに指定することで、サーバーサイドでリクエストの改ざんを検知する仕組みになっています。
応用が効かない負荷テストツールでは、この手のシグネチャ生成をサポートするのが手間な事があるのですが、
GatlingではScalaによってシグネチャ生成処理を自由に書くことができます。
Gatlingは、Java製の非同期HTTPクライアントとして有名なAsyncHttpClient を使用しています。
AsyncHttpClientはシグネチャ生成のためのフック処理をサポートしており、GatlingにおいてもAsyncHttpClientが提供しているSignatureCalculator を実装することで、リクエストの直前にシグネチャを生成し、任意のHTTPリクエストヘッダーに値を設定する事が可能となっています。
SignatureCalculator
は以下のような定義になっています。
public interface SignatureCalculator { void calculateAndAddSignature(Request request, RequestBuilderBase<?> requestBuilder); }
第一引数の request
には、Gatling DSLで構築したURLやリクエストヘッダー/ボディが設定されています。
この request
の値を使用して、シグネチャを生成し、第二引数の requestBuilder
に対してシグネチャ用のHTTPリクエストヘッダーを設定することになります。
以下に、一例を示します。SignatureCalculator
はsingle method interfaceなので、ここでは関数として定義します。
import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec import com.hunorkovacs.koauth.service.Arithmetics object Utils { // シグネチャ生成用秘密鍵 val secret = sys.env.getOrElse("API_SECRET", "your secret") /** シグネチャ生成およびHTTPリクエストヘッダーへの設定 */ def generateAndAddSignature(request: Request, rb: RequestBuilderBase[_]): Unit = { val token = request.getHeaders().get("Authorization").replace("Bearer ", "") val signingKey = s"$token&$secret" val method = request.getMethod() val url = request.getUrl() val encodedUrl = Arithmetics.urlEncode(url) // getByteDataでbodyのバイト配列が取得できないためボディの文字列からバイト配列を取得 val body = Option(request.getStringData()).map(_.getBytes()).map(new String(_)).getOrElse("") val encodedBody = Arithmetics.urlEncode(body) val baseStr = s"$method&$encodedUrl&$encodedBody".toUpperCase() val mac = Mac.getInstance("HmacSHA256") val key = new SecretKeySpec(signingKey.getBytes("utf-8"), "HmacSHA256") mac.init(key) val signature = mac.doFinal(baseStr.getBytes("utf-8")) // bytes to Hex val sigHex = signature.map("%02x" format _).mkString rb.addHeader("X-Signature", sigHex) rb.addHeader("X-Signature-Method", "HMAC-SHA256") }
このgenerateAndAddSignature
は、以下のようにして使用します。
val getPlayerStatus = http("player status") .get("/v1/players/me") .header("Authorization", "Bearer " + authToken) .signatureCalculator(generateAndAddSignature _) // eta-expansionでメソッドを値にして、signatureCalculatorメソッドに渡す .check(status.in(200 to 210))
まとめ
今回は、Gatlingでのシグネチャ検証にどのように対応するかをHMACの生成の一例とともにご紹介しました。
Scalaで自由にアルゴリズムを実装できるのは、Gatlingの強みです。
負荷テスト用にシグネチャ検証をバイパスするのではなく、シグネチャ検証も含めた負荷テストを実施するように心がけましょう。