【AWS】AWS APIの認証・認可の仕組みを理解する【Signature V4】

# はじめに
Amplifyを利用してアプリケーションを作る場合、Cognito User Pool／Cognito Identity Pool（Cognito Federated Identities）を利用してAPI GatewayでAuthorizationする方法が暗黙裡にデフォルトになっているように見えます。
よく使われるであろうと思われる割には、その仕組みについての説明があまり見当たらなかったので解説記事を書こうとしましたが、前提となる事項があまりにも多いことが分かったので、個別の記事として分離しました。

本稿では、AccessKeyIdやSecretAccessKeyの使われ方、AWS APIにおけるSignature V4の仕組みなどについて解説します。
API GatewayのAuthorization方式として「AWS_IAM」という項目を選択できますが、本稿の内容を理解することがまず必要となります。

AmplifyのAuthorization方式「AWS_IAM」に関する説明は、下記記事にまとめています。
[【Amplify】APIのAuthorization方式「AWS_IAM」を理解する#1　～解説編～【AWS】](https://qiita.com/tmiki/items/061b321199c9ff986e71)

# TL;DR
- AWSが提供する機能の殆ど、最終的にRESTFul APIとなる（本稿ではAWS APIと呼ぶ）
- AWS APIの実行には、Signature V4による署名が必要
- 各AWS APIは、Signature V4により、APIリクエスト発行者のAuthentication・Authorizationを実施する
- Signature V4の署名には、AWS Security Credentialsが必要
- AWS Security Credentialsには、long-termなものとshort-termなものの2種類ある
    - long-termなAWS Security Credentials
        - これはAccessKeyIdとSecretAccessKeyで構成される
        - その代表格はIAM Userに紐づくAccessKeyId/SecretAccessKey
    - short-termなAWS Security Credentials
        - これはAccessKeyIdとSecretAccessKeyとSessionTokenで構成される
        - IAM RoleをAssumeRoleして取得する利用方法が代表格

# 解説
## AWS API
アプリケーションプログラムから各AWSサービスを利用する場合、大体においてAWS SDKやその他フレームワーク／ライブラリを利用するものと思います。AWSサービスの仕組みを紐解いていくと、それらの機能は最終的にはRESTFulなAPI群で提供されていることが分かります。（※RDSなどのマネージドなサーバサービスはこの限りではない）

例えばDynamoDBであれば、最終的には下記のlow-levelなAPIに行きつきます。

Welcome - Amazon DynamoDB
https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/Welcome.html

S3も同様、最終的には下記のようなAPI群で提供されています。本稿では、このようなAWSサービスを利用するためのAPIをAWS APIと呼ぶことにします。

Amazon S3 REST API Introduction - Amazon Simple Storage Service
https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html

基本的には殆どのAWS APIは認証・認可が必要となります。例えばS3 APIリファレンスの冒頭には以下のように記載があります。

> Requests to Amazon S3 can be authenticated or anonymous. Authenticated access requires credentials that AWS can use to authenticate your requests. **When making REST API calls directly from your code, you create a signature using valid credentials and include the signature in your request.** For information about various authentication methods and signature calculations, see Authenticating Requests (AWS Signature Version 4).
> 
> Making REST API calls directly from your code can be cumbersome. It requires you to write the necessary code to calculate a valid signature to authenticate your requests. 

※強調は引用者による

認証・認可が必要なS3 APIの実行の際には、有効なCredentialsを利用してSignatureを作成し、これをリクエストに付与せよ、と書いてあります。
この署名を作成する方法の1つが、いわゆる**Signature V4（Sign V4）**です。Signature V3以前のバージョンもありますが忘れてください。

AWSはREST APIのリクエストを受け付けた際に、以下のようなチェックを行います。これらチェックするための仕組みが署名です。

- リクエストの内容が改ざんされていないこと（Integrity）
- 正統なユーザから発行されたこと（Authenticate）
- 当該ユーザは対象APIを実行する権限があること（Authorize）

では次項から、これらの仕組みについて見ていきます。

## AWS Security Credentials（AccessKeyId/SecretAccessKey）
まず、先ほどの文章にあったCredentialsとは何でしょうか。
その正体は、**AccessKeyIdとSecretAccessKeyのペア**です。

通常、AWS CLIを利用する際には、自身が利用するIAM Userアカウントに紐づくAccessKeyIdとSecretAccessKeyを発行して、 ~/.aws/credentials ファイルに設定しますが、これがそうです。1つのIAM Userにひもづき、当該ユーザの権限でAWS APIを実行することができます。
AWS CLIを利用して各AWSの機能を利用する際、AWS CLI内で署名が生成され、付与されています。

なお、ドキュメントによってはこの呼称が結構ブレていて、「AWS Credentials」と呼ばれたり「Access Keys」と呼ばれたり、単に「security credentials」と呼ばれたりしています。本稿では「**AWS Security Credentials**」で統一します。

AWS Security Credentials - AWS General Reference
https://docs.aws.amazon.com/general/latest/gr/aws-security-credentials.html

Understanding and Getting Your Security Credentials - AWS General Reference
https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys

## AWS Temporary Security Credentials（AccessKeyId/SecretAccessKey/SessionToken）
AWS Security Credentialsには、**Permanentなもの**と**Temporaryなもの**の2種類あります。（ドキュメント上、**long-ter**、**short-term**と表現されている個所もあります。）
前者は上述した通りで、後者は文字通り一時的に発行されるAWS Security Credentialsとなります。
大体のドキュメントにおいて、Temporary Security Credentialsと呼ばれているようです。本稿では「**AWS Temporary Security Credentials**」で統一します。

Temporary Security Credentials
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html

具体的には、IAM RoleをAsssumeRoleしてこれを取得する、という利用方法が多いかと思います。

AssumeRole - AWS Security Token Service
https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html

具体的な仕組みは省きますが、IAM RoleをAssumeRoleして**一時的なAWS Security Credentialsを得る場合、AccessKeyIdとSecretAccessKeyのペアに加え、SessionTokenというものが追加で発行**されます。
AWS Temporary Security Credentialsを利用してAWS APIを発行する際には、このSessionTokenも併せて必要となります。

## Signature V4
最後に、署名を作るための仕様である、Signature V4について見ていきます。

Signature Version 4 Signing Process - AWS General Reference
https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html

細かい仕様は措いておくとして、基本的には以下のような仕組みとなります。

1. AWS APIを発行する際に、そのリクエスト内容を元に署名を作る。SecretAccessKeyを鍵として用いて署名を作成する。
2. AWS APIに当該署名を付与してリクエストする。
3. 当該リクエストを受け取ったAWSサービスは、1と同一の手順で署名を生成し、リクエストに付与されたものと照合する。


署名の生成・付与方法は以下の4つのステップに分けられます。
**利用するAWS Security CredentialsがPermanent／Temporaryなものであるかに関わらず、署名生成の方法はどちらも同一**となります。

> **How Signature Version 4 works**
> 
> 1. Create a canonical request.
> 2. Use the canonical request and additional metadata to create a string for signing.
> 3. Derive a signing key from your AWS secret access key. Then use the signing key, and the string from the previous step, to create a signature.
> 4. Add the resulting signature to the HTTP request in a header or as a query string parameter.

下記の通り、生成した署名はHTTPリクエストヘッダ「Authorization」として付与するか、GETメソッドを使う場合はそのクエリパラメータに付与するか、いずれかの方法で付与する必要があります。

Task 4: Add the Signature to the HTTP Request - AWS General Reference
https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html

> After you calculate the signature, add it to the request. You can add the signature to a request in one of two ways:
> - An HTTP header named Authorization
> - The query string
> You cannot pass signing information in both the Authorization header and the query string.

AWS Temporary Security Credentialsを利用する場合、HTTTPリクエストヘッダ「X-Amz-Security-Token」としてSessionTokenを付与する必要があります。
Signature V4の署名作成方法自体は、PermanentなAWS Security Credentialsのそれと違いはありませんが、AWSサービスによってはそのリクエスト内容にSessionTokenを含める必要があるケースがあるようで、この場合は署名生成の際に少し注意が必要です。

> You can use temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign a request. The process is the same as using long-term credentials, but requires an additional HTTP header or query string parameter for the security token. The name of the header or query string parameter is X-Amz-Security-Token, and the value is the session token (the string you received from AWS STS when you obtained temporary security credentials).
> 
> When you add the X-Amz-Security-Token parameter to the query string, some services require that you include this parameter in the canonical (signed) request. For other services, you add this parameter at the end, after you calculate the signature. For details, see the API reference documentation for that service.


AWS Temporary Security Credentialsの利用については、下記ドキュメントにも記載があります。

Using Temporary Credentials With AWS Resources - AWS Identity and Access Management
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html

> You can use temporary security credentials to make programmatic requests for AWS resources using the AWS CLI or AWS API (using the AWS SDKs). The temporary credentials provide the same permissions that you have with use long-term security credentials such as IAM user credentials. However, there are a few differences:
> 
> - When you make a call using temporary security credentials, the call must include a session token, which is returned along with those temporary credentials. AWS uses the session token to validate the temporary security credentials.
> -The temporary credentials expire after a specified interval. After the credentials expire, any calls that you make with those credentials will fail, so you must get a new set of credentials.
> 
> ※以降省略

## Signature V4の詳細


より具体的な実装については、以下のサイトが参考になりました。
実際のリクエスト内容をどのように処理して署名を生成するのか、具体的に理解できると思います。

AWS REST リクエストの署名についてメモ – Siguniang's Blog
https://siguniang.wordpress.com/2014/11/23/evolution-of-aws-signing-signatures/


# おわりに
AWS APIがそのAPIリクエスト内容をチェックする方法について見てきました。
Cognito Identity PoolとAPI GatewayのAWS_IAM Authorization方式を組み合わせて利用する場合、この仕組みが関わってきますので、理解しておく必要があります。