【AWS】【Serverless Framework】API キーの設定（リクエストの制限の設定）

# やったこと

この記事では Serverless Framework で API キーの設定方法や、設定した場合の API の挙動について書いていきたいと思います。

結局のところ serverless.yml で記述するそれらの設定は API Gateway の API キーの設定に反映されます。

# serverless.yml

serverless.yml に記述することで API キーの設定が行うことができます。

今回は serverless.yml は以下のように設定しました。


```yaml:serverless.yml
provider:
  # （省略）
  apiKeys:
    - free: # 使用量プラン
        - name: ${opt:stage, self:provider.stage}-free-key # key 名
          value: a123456789012345678901234567890 # 30-128文字の英数字
    - mogemoge: # 複数のプランを設定することが可能
        - ${opt:stage, self:provider.stage}-mogemoge-key # name, value を指定しないと value は自動生成される
  usagePlan:
    - free:
        quota:
          limit: 1000 # API の呼び出しを行える最大回数
          offset: 2 # API の呼び出し回数の初期値（通常は 0 回を指定する）
          period: MONTH # DAY or WEEK or MONTH
        throttle:
          rateLimit: 2 # 1 秒あたりに処理できる API リクエスト数
          burstLimit: 3 # 同時に処理できる最大リクエスト数
    - mogemoge:
        quota:
          limit: 50000
          offset: 1
          period: MONTH
        throttle:
          burstLimit: 2000
          rateLimit: 1000

# （省略）

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
          private: true # API キーを有効にするエンドポイントは true にする
```

apiKeys, quota, throttle の設定についてコメントで注釈をいれました。

さらに詳しいことは以下を参考にしてください。

【参考】
・[API Gateway の API Key を調べてみた](https://dev.classmethod.jp/articles/api-gateway-usage-plan/)
・[API リクエストを調整してスループットを向上させる](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-request-throttling.html)

**個人的に思うこととして、API キーの値は自動生成させるのではなく、値を固定して運用することがほとんどだと思います。**

デプロイごとに API キーが変わると、今まで使用していた API キーでアクセスするとエラーになってしまうので、もし、自動生成としている場合、クライアント側と API 側は常に同時にアップデートしなければならない制約が設けられます。

かといって、『ソースコード上に API キーの値を直接書くことはセキュリティ上よくないのでは？』と思いますが、そもそも論として、API キーは唯一の認証には使用してはいけなく、リクエストの制限を行うユーザーをグループとして管理するために用いるものであるので、ソースコード上にベタに書いても問題ないと思います。（もちろん、認証の仕組みは別に設ける必要がありますが。）

# API キーの作成

上記のように serverless.yml を設定して `sls deploy` するだけです。

```
$ sls deploy
```

デプロイに成功するとコンソールの最後の方に API キーが出力されると思います。

削除は `sls remove` で削除

# API キーの確認

ちなみに API キーの確認は `sls info` コマンド実行すれば、いつでも可能です。
もちろん sls deploy コマンド実行時のコンソールに API キーの値が出力されます。

```
$ sls info
```
![aaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/ebbfd47c-2aa4-e526-c2cc-23b1601f3b28.png)

# API の挙動

実際に API キー を設定した場合の API の挙動について確認してみました。
以下のケースで行ってみました。

- x-api-key を設定していない場合
- x-api-key が不正な場合
- x-api-key が正常な場合
- quota の上限を超えた場合
- throttle の上限を超えた場合

API キーをリクエストに設定する場合は、リクエストヘッダーの **`x-api-key`** というキーに対して、API キーの値を設定します。

## x-api-key を設定していない場合

・方法： `x-api-key` をリクエストヘッダーで設定しない
![aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/f4bee96d-02b8-2c4f-68a3-abcc0111e01c.png)
【結果】
・ Stats: 403 Forbidden
・ Response body: {"message":"Forbidden"}

## x-api-key が不正な場合

・方法： `x-api-key` に不正な値（hogehoge）を使用する
![bb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/da805249-be4c-f53b-2c7d-167bdf29f4bd.png)
【結果】
・ Stats: 403 Forbidden
・ Response body: {"message":"Forbidden"}

## x-api-key が正常な場合

・方法： `x-api-key` に正常な値（a123456789012345678901234567890）を使用する
![cc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/8c52e60f-eda6-98ca-01b7-50d84a28e486.png)
【結果】
・Stats: 200 OK
・Response body: 正常なレスポンス

## quota の上限を超えた場合

・方法： quota の設定をかなり低くしてリクエスト回数の上限を超えてリクエストする
![d 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/0ae00431-564e-00bc-9ae8-c8152b0c2455.png)
【結果】
・Stats: 429 Too Many Requests
・Response body: **{"message":"Limit Exceeded"}**

## throttle の上限を超えた場合

・方法： throttle の上限をかなり低く設定して 100 件連続でリクエストを投じる
![qqqqq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/625824d1-d43d-be46-7f4d-83eb8876b0fd.png)
【結果】
・Stats: 429 Too Many Requests
・Response body: **{"message":"Too Many Requests"}**

## 検証結果

- x-api-key を設定していない場合
  - Stats: 403 Forbidden
  - Response body: {"message":"Forbidden"}
- x-api-key を設定していない場合
  - Stats: 403 Forbidden
  - Response body: {"message":"Forbidden"}
- x-api-key が正常な場合
  - Stats: 200 OK
  - Response body: 正常なレスポンス
- quota の上限を超えた場合
  - Stats: 429 Too Many Requests
  - Response body: **{"message":"Limit Exceeded"}**
- throttle の上限を超えた場合
  - Stats: 429 Too Many Requests
  - Response body: **{"message":"Too Many Requests"}**

quota、throttle の上限を超えた場合は両方とも `Stats: 429 Too Many Requests` でしたが、Response body の値が違うようですね。

# API Gateway コンソールでの確認

serverless.yml で記述した設定は API Gateway の API キーの設定に反映されます。
![スクリーンショット 2020-12-12 21.10.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/edb7d912-c0cc-f8c3-b7d4-f9d45b7f9292.png)
使用量も確認することができます。
![スクリーンショット 2020-12-12 21.49.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/e0a29e48-334e-af04-b1d2-db5fd6e7aaed.png)

# 参考文献

- Serverless Framework 公式ドキュメント
  - [Setting API keys for your Rest API](https://www.serverless.com/framework/docs/providers/aws/events/apigateway#setting-api-keys-for-your-rest-api)
- AWS 公式ドキュメント
  - [API キーを使用した使用量プランの作成と使用](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-api-usage-plans.html)
- クラスメソッドでの解説
  - [API GatewayのAPIキーと使用量プランについて調べてみた](https://dev.classmethod.jp/articles/try-api-gateway-usage-plan/)
  - [API Gateway の API Key を調べてみた](https://dev.classmethod.jp/articles/api-gateway-usage-plan/)