この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
CX事業本部の佐藤です。
API GatewayでIAM認証を設定して、Node.jsでSigV4署名を実装する機会がありましたので、知見としてまとめます。
API Gatewayの認証方式
まずは、API Gatewayの認証方式をおさらいしてみます。API Gatewayでは、APIに対して以下のような認証方式が存在します。
- Cognitoを使った認証
- サードパーティの認証基盤(Auth0など)とLambda Authorizerを使った認証
- IAM認証
この記事では、この中のIAM認証に焦点を当てていきます。
そもそもSigV4署名とは?
正式名称は、AWS Signature V4 署名で、バージョン1〜4まであります。現在はバージョン4を使うのが推奨です。IAMのアクセスキーとハッシュ関数など用いて、AWSへのHTTPリクエストに署名する方法です。普段私たちが使っているAWS SDK
などは、この署名を内部的に設定してくれているため、使う側の私たちはこの署名を意識することはありません。私たちが、SigV4署名を意識する必要があるのは、SDKを使わないでAWSにリクエストする場合、例えば、AWS SDK
がないプログミング言語を使用している場合や、今回のようにAPI GatewayにIAM認証を設定するような場合です。
わざわざ自前で実装しなくても、API GatewayのSDK生成機能を使えばいいのでは?
API GatewayにはSDKの生成機能があり、それを使うことでSDK側がSigV4署名を行ってくれます。ただこのSDKの生成機能は、開発、テスト中にAPIを変更するたびに生成し直す必要があり、生成後は、Lambda Layerの更新や開発リポジトリに組み込むなど、一手間必要なため、今回は自前で実装する選択肢にしました。
Node.jsでSigV4署名を作成する
API GatewayでIAM認証を設定する
まずは、API GatewayでIAM認証を設定したモックAPIを作成します。API Gatewayのコンソールに移動し、APIを作成します。
作成したら、ルートパスを選択しメソッドの作成をクリックします。
POSTを選択してチェックマークをクリックし、Mockを選択し、保存をクリックします。
これで、モックAPIができました。次にIAM認証を設定します。メソッドリクエストをクリックします。
認可で AWS_IAM
を選択します。
これで、モックAPIのIAM認証の設定が完了しました。
SigV4署名ソースコード
次に、SigV4署名をしてHTTPリクエストをしてみます。AWS公式のドキュメントにあるように、SHA256やらHMACとかのハッシュ関数を使ってガリガリ書くのもいいんですが、aws-sdk
のaws-sdk/lib/core
パッケージにSigners.V4
クラスがあり、これを使うことでSigV4署名を作ることができます
以下はNode.jsでSigV4署名を作成するコードです。
index.js
const core = require('aws-sdk/lib/core');
const aws = require('aws-sdk');
// アクセスキーとシークレットアクセスキーを設定
const accessKey = 'XXXX';
const secretKey = 'XXXX';
const credential = new aws.Credentials(accessKey, secretKey);
main();
function main() {
// サービス名は、API GatewayのAPIの場合は、execiute-api固定です。
const serviceName = "execute-api";
// Signers.V4クラスのコンストラクタに渡すオプションを作成します。
const options = {
// api gatewayのURL
url: 'https://XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/',
headers: {}
};
// api gatewayのURLからホスト、パス、クエリストリングを抽出
const parts = options.url.split('?');
const host = parts[0].substr(8, parts[0].indexOf("/", 8) - 8);
const path = parts[0].substr(parts[0].indexOf("/", 8));
const querystring = parts[1];
// V4クラスのコンストラクタの引数に沿う形でoptionsを作成
const now = new Date();
options.headers.host = host;
options.pathname = () => path;
options.methodIndex = 'post';
options.search = () => querystring ? querystring : "";
options.region = 'ap-northeast-1';
options.method = 'POST';
// V4クラスのインスタンスを作成
const signer = new core.Signers.V4(options, serviceName);
// SigV4署名
signer.addAuthorization(credential, now);
// 署名されたヘッダーを出力
console.log(options.headers);
}
上記のコードを実行します。
node index.js
実行すると以下のようなHTTPヘッダの文字列がコンソール上に出力されます。これがSigV4の署名です。このヘッダーを使ってリクエストを送ることでIAM認証を突破することができます。
{
host: 'XXXXXXX.execute-api.ap-northeast-1.amazonaws.com',
'X-Amz-Date': '20200114T105609Z',
Authorization: 'AWS4-HMAC-SHA256 Credential=XXXXXX, SignedHeaders=host;x-amz-date, Signature=XXXXXX'
}
動作確認
Postmanを使って、API Gatewayにリクエストを送ってみます。まずは、ヘッダに署名を付与しない状態でAPIを叩いてみます。
IAM認証をかけているため、Missing Authentication Token
となりました。
次に、先ほど出力された署名ヘッダを入力して、リクエストを送ってみます。
今度は、Missing Authentication Token
とはでませんでした。うまくいってそうです。
まとめ
最初は、AWS SDKにAPI Gatewayで作成したAPIを叩く関数があって、それを使えば簡単にできるのかなと思っていたんですが、そのような関数はありませんでしたので、今回のようにライブラリを使って実装しました。API GatewayにIAM認証をかける方に参考になれば幸いです。