k6でBasic認証とDigest認証のサイトに負荷をかける

# はじめに
サイトがBasic認証やDigest認証を設定していた場合、どうしたら良いか気になったので調べて試してみました。
ただ実行するだけだとピンとこない部分もあると思うので、curlコマンドのどの実行に相当するのかも併せて記載してます。

# 事前確認
ますは Basic認証 が設定されているサイトを確認します。
コマンドラインで確認すると以下が確認できます。
```bash:401 UNAUTHORIZED
$ curl -i https://httpbin.test.k6.io/basic-auth/user/passwd
HTTP/1.1 401 UNAUTHORIZED
Date: Sun, 11 Sep 2022 06:29:39 GMT
Content-Length: 0
Connection: keep-alive
WWW-Authenticate: Basic realm="Fake Realm"
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
```
```bash:200 OK
$ curl -i --user 'user:passwd' https://httpbin.test.k6.io/basic-auth/user/passwd
HTTP/1.1 200 OK
Date: Sun, 11 Sep 2022 06:40:43 GMT
Content-Type: application/json
Content-Length: 47
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "authenticated": true, 
  "user": "user"
}
```

ブラウザで確認するとこのような感じになります。
![スクリーンショット 2022-09-11 15.35.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/2da57e26-d3b8-5938-fb2e-ddd1e3403b9b.png)
![スクリーンショット 2022-09-11 15.36.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/73d232ab-5126-158d-7cf4-31bbf4b728f6.png)

Digest認証のブラウザアクセスは以下です。
![スクリーンショット 2022-09-16 21.31.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/e587798f-b7f3-f7d1-a0e7-5d74c3b83334.png)
![スクリーンショット 2022-09-16 21.31.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/1dcc2aa6-3ea0-8e16-eba6-cb4ad587dc01.png)

それでは実際に k6 を実行したいと思います。

# 負荷テスト
Basic認証とDigest認証に対してのBasic認証のサイトには2つの方法でアクセスができます。
## 方法1. URL埋め込み
認証情報をURLに埋め込む方法です。
```js:basic-auth01.js
import encoding from 'k6/encoding';
import http from 'k6/http';
import { check } from 'k6';

const username = 'user';
const password = 'passwd';

export default function () {
  const credentials = `${username}:${password}`;

  const url = `https://${credentials}@httpbin.test.k6.io/basic-auth/user/passwd`;

  let res = http.get(url);

  // Verify response
  check(res, {
    'status is 200': (r) => r.status === 200,
    'is authenticated': (r) => r.json().authenticated === true,
    'is correct user': (r) => r.json().user === username,
  });
}
```
`ID`と`PASS`を`:`で繋げたという組み合わせで curlコマンド に置き換えると以下になります。
```bash:curl
$ curl -i  https://user:passwd@httpbin.test.k6.io/basic-auth/user/passwd
```
それではスクリプトを実行します。
```bash:実行
% k6 run basic-auth01.js

          /\      |‾‾| /‾‾/   /‾‾/   
     /\  /  \     |  |/  /   /  /    
    /  \/    \    |     (   /   ‾‾\  
   /          \   |  |\  \ |  (‾)  | 
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: basic-auth01.js
     output: -

  scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
           * default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)


running (00m00.8s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m00.8s/10m0s  1/1 iters, 1 per VU

     ✓ status is 200
     ✓ is authenticated
     ✓ is correct user

     checks.........................: 100.00% ✓ 3       ✗ 0
     data_received..................: 5.7 kB  7.5 kB/s
     data_sent......................: 515 B   670 B/s
     http_req_blocked...............: avg=579.44ms min=579.44ms med=579.44ms max=579.44ms p(90)=579.44ms p(95)=579.44ms
     http_req_connecting............: avg=183.25ms min=183.25ms med=183.25ms max=183.25ms p(90)=183.25ms p(95)=183.25ms
     http_req_duration..............: avg=185.8ms  min=185.8ms  med=185.8ms  max=185.8ms  p(90)=185.8ms  p(95)=185.8ms 
       { expected_response:true }...: avg=185.8ms  min=185.8ms  med=185.8ms  max=185.8ms  p(90)=185.8ms  p(95)=185.8ms 
     http_req_failed................: 0.00%   ✓ 0       ✗ 1
     http_req_receiving.............: avg=287µs    min=287µs    med=287µs    max=287µs    p(90)=287µs    p(95)=287µs   
     http_req_sending...............: avg=227µs    min=227µs    med=227µs    max=227µs    p(90)=227µs    p(95)=227µs   
     http_req_tls_handshaking.......: avg=198.35ms min=198.35ms med=198.35ms max=198.35ms p(90)=198.35ms p(95)=198.35ms
     http_req_waiting...............: avg=185.29ms min=185.29ms med=185.29ms max=185.29ms p(90)=185.29ms p(95)=185.29ms
     http_reqs......................: 1       1.30138/s
     iteration_duration.............: avg=766.35ms min=766.35ms med=766.35ms max=766.35ms p(90)=766.35ms p(95)=766.35ms
     iterations.....................: 1       1.30138/s
```

## 方法2. Authorization ヘッダ指定
Authorization ヘッダを使ったアクセス方法です。
```bash:basic-auth02.js
import encoding from 'k6/encoding';
import http from 'k6/http';
import { check } from 'k6';

const username = 'user';
const password = 'passwd';

export default function () {
  const credentials = `${username}:${password}`;

  const encodedCredentials = encoding.b64encode(credentials);
  const options = {
    headers: {
      Authorization: `Basic ${encodedCredentials}`,
    },
  };

  let res = http.get(`https://httpbin.test.k6.io/basic-auth/${username}/${password}`, options);

  check(res, {
    'status is 200': (r) => r.status === 200,
    'is authenticated': (r) => r.json().authenticated === true,
    'is correct user': (r) => r.json().user === username,
  });
}
```
curlコマンド では以下のようになります。
```bash:curl
$ ENC=`echo -n "user:passwd" | base64` | curl -i  https://httpbin.test.k6.io/basic-auth/user/passwd -H "Authorization:Basic ${ENC}"
```
それではスクリプトを実行します。
```bash:実行
% k6 run basic-auth02.js

          /\      |‾‾| /‾‾/   /‾‾/   
     /\  /  \     |  |/  /   /  /    
    /  \/    \    |     (   /   ‾‾\  
   /          \   |  |\  \ |  (‾)  | 
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: basic-auth02.js
     output: -

  scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
           * default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)


running (00m01.0s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m01.0s/10m0s  1/1 iters, 1 per VU

     ✓ status is 200
     ✓ is authenticated
     ✓ is correct user

     checks.........................: 100.00% ✓ 3        ✗ 0  
     data_received..................: 5.7 kB  5.6 kB/s
     data_sent......................: 515 B   505 B/s
     http_req_blocked...............: avg=828.2ms  min=828.2ms  med=828.2ms  max=828.2ms  p(90)=828.2ms  p(95)=828.2ms 
     http_req_connecting............: avg=181.86ms min=181.86ms med=181.86ms max=181.86ms p(90)=181.86ms p(95)=181.86ms
     http_req_duration..............: avg=185.46ms min=185.46ms med=185.46ms max=185.46ms p(90)=185.46ms p(95)=185.46ms
       { expected_response:true }...: avg=185.46ms min=185.46ms med=185.46ms max=185.46ms p(90)=185.46ms p(95)=185.46ms
     http_req_failed................: 0.00%   ✓ 0        ✗ 1  
     http_req_receiving.............: avg=1.32ms   min=1.32ms   med=1.32ms   max=1.32ms   p(90)=1.32ms   p(95)=1.32ms  
     http_req_sending...............: avg=696µs    min=696µs    med=696µs    max=696µs    p(90)=696µs    p(95)=696µs   
     http_req_tls_handshaking.......: avg=201.94ms min=201.94ms med=201.94ms max=201.94ms p(90)=201.94ms p(95)=201.94ms
     http_req_waiting...............: avg=183.44ms min=183.44ms med=183.44ms max=183.44ms p(90)=183.44ms p(95)=183.44ms
     http_reqs......................: 1       0.981191/s
     iteration_duration.............: avg=1.01s    min=1.01s    med=1.01s    max=1.01s    p(90)=1.01s    p(95)=1.01s   
     iterations.....................: 1       0.981191/s
     vus............................: 1       min=1      max=1
     vus_max........................: 1       min=1      max=1
```

## Digest認証

```js:digest-auth.js
import http from 'k6/http';
import { check } from 'k6';

const username = 'user';
const password = 'passwd';

export default function () {
  const credentials = `${username}:${password}`;
  const res = http.get(
    `https://${credentials}@httpbin.test.k6.io/digest-auth/auth/${username}/${password}`,
    { auth: 'digest' }
  );

  check(res, {
    'status is 200': (r) => r.status === 200,
    'is authenticated': (r) => r.json().authenticated === true,
    'is correct user': (r) => r.json().user === username,
  });
}
```
curlコマンド では以下のようになります。
```bash:curl
$ curl --digest --user "user:passwd" "https://httpbin.test.k6.io/digest-auth/auth/user/passwd"
```

```bash:実行
$ k6 run digest-auth.js                                                                       

          /\      |‾‾| /‾‾/   /‾‾/   
     /\  /  \     |  |/  /   /  /    
    /  \/    \    |     (   /   ‾‾\  
   /          \   |  |\  \ |  (‾)  | 
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: digest-auth.js
     output: -

  scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
           * default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)


running (00m00.9s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m00.9s/10m0s  1/1 iters, 1 per VU

     ✓ status is 200
     ✓ is authenticated
     ✓ is correct user

     checks.........................: 100.00% ✓ 3        ✗ 0
     data_received..................: 6.3 kB  6.8 kB/s
     data_sent......................: 988 B   1.1 kB/s
     http_req_blocked...............: avg=245.27ms min=5µs      med=245.27ms max=490.55ms p(90)=441.49ms p(95)=466.02ms
     http_req_connecting............: avg=90.66ms  min=0s       med=90.66ms  max=181.33ms p(90)=163.2ms  p(95)=172.27ms
     http_req_duration..............: avg=216.54ms min=201.39ms med=216.54ms max=231.69ms p(90)=228.66ms p(95)=230.17ms
       { expected_response:true }...: avg=216.54ms min=201.39ms med=216.54ms max=231.69ms p(90)=228.66ms p(95)=230.17ms
     http_req_failed................: 0.00%   ✓ 0        ✗ 2
     http_req_receiving.............: avg=164.5µs  min=137µs    med=164.5µs  max=192µs    p(90)=186.5µs  p(95)=189.25µs
     http_req_sending...............: avg=38µs     min=17µs     med=38µs     max=59µs     p(90)=54.8µs   p(95)=56.89µs 
     http_req_tls_handshaking.......: avg=99.22ms  min=0s       med=99.22ms  max=198.45ms p(90)=178.61ms p(95)=188.53ms
     http_req_waiting...............: avg=216.34ms min=201.18ms med=216.34ms max=231.49ms p(90)=228.46ms p(95)=229.98ms
     http_reqs......................: 2       2.157921/s
     iteration_duration.............: avg=924.61ms min=924.61ms med=924.61ms max=924.61ms p(90)=924.61ms p(95)=924.61ms
     iterations.....................: 1       1.07896/s
```


# おわりに
負荷テストを行う際、認証が掛かってる場合は多々あると思います。
一時的に解除することが可能な場合は良いですが、そういうことが難しい場合などにとても有効な手段だと思います。


# 参考

https://k6.io/docs/examples/http-authentication/