Amazon S3 + Amazon API Gatewayの構成におけるアクセス制御方法を比較してみた

AWS

2023.12.14

Topics

はじめに

この記事は NHN テコラス Advent Calender 2023の14日目の記事です。

こんにちは、henge です。
今回は API Gateway で REST API を用いて、AWS 外部から S3 へアクセスするアーキテクチャのアクセス制御方法についてまとめました。

API Gateway と S3 によるアーキテクチャは、S3 に AWS 外部からアクセスする手法としてよく用いられます。
AWS re:Post の記事でも以下の様に API Gateway を介して S3 にアクセスする方法が紹介されています。
API ゲートウェイを使用して Amazon S3 に画像や PDF ファイルをアップロードする | AWS re:Post

今回はそれぞれの外部ユーザーに対して 1 つの特定のフォルダにのみアクセスを許可するというユースケースを想定します。
以下で詳しく説明します。

想定するユースケース

一般的に S3 バケットでは格納されるオブジェクトをその特徴に応じてフォルダで分類します。
(S3 にフォルダという機能はありませんが、便宜上フォルダと表現します。)

例えば、下の画像の様に利用ユーザー毎にフォルダを配置し、API のメソッド毎に 1 つのフォルダへアクセスすることが考えられます。

この様なユースケースの場合、ユーザー A がユーザー B 用のフォルダにアクセスできるという状態はセキュリティの観点上好ましくありません。
ユーザー A はユーザー A 専用のフォルダにのみアクセス可能という状態が望ましいですね。

今回はこの様なアクセス制御を実現するためにはどのような設定を適用するべきなのかご紹介します。

結論

早速結論を見ていきましょう。
API Gateway には以下のアクセス制御機能が備わっています。
※ S3 に備わっているアクセス制御機能は本ユースケースでは利用できません。詳細は後述します。

  • API キー
  • リソースポリシー
  • Lambda オーソライザー
  • Cognito オーソライザー

これらの内、推奨方法と本ユースケースには適さない方法は以下の通りです。

  • 送信元 IP アドレスを把握できる場合の推奨方法
    リソースポリシー

  • 送信元 IP アドレスを把握できない場合の推奨方法
    Lambda オーソライザー, Cognito オーソライザー

  • 本ユースケースには適さない方法
    API キー

以下でこれらのアクセス制御方法がどのようなものかご紹介します。

アクセス制御方法紹介

本ユースケースにおけるアクセス制御で重要な点は以下の通りです。

  • 単一 S3 バケット内の特定のフォルダにのみアクセスを許可すること
    → 細かいアクセス制御が必要
  • ユーザーは AWS 外からアクセスすること
    → ユーザーを IAM ユーザーなどの AWS 内の情報で識別できない

以下ではこの 2 点に注目してアクセス制御方法を比較します。

リソースポリシー

リソースポリシーとはリソースにアタッチされる IAM ポリシーのようなものです。
アタッチ先がユーザー側かリソース側かという相違点はありますが、権限を記述する書式については大きな差異はありません。

例えば、以下のリソースポリシーでは、送信元 IP アドレスが192.0.2.0/24198.51.100.0/24の範囲内の API Gateway へのアクセスを拒否します。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": "*",
			"Action": "execute-api:Invoke",
			"Resource": ["execute-api:/*"]
		},
		{
			"Effect": "Deny",
			"Principal": "*",
			"Action": "execute-api:Invoke",
			"Resource": ["execute-api:/*"],
			"Condition": {
				"IpAddress": {
					"aws:SourceIp": ["192.0.2.0/24", "198.51.100.0/24"]
				}
			}
		}
	]
}

出典: API Gateway リソースポリシーの例 – Amazon API Gateway

本ユースケースにおけるリソースポリシーの特長は以下の通りです。

  • Resource 要素で API のメソッド単位での指定が可能
  • Condition 要素で送信元 IP アドレスという AWS 外のユーザー情報の指定が可能

従って、AWS 外から S3 へアクセスする際にフォルダ毎のアクセス制御をしたいという本要件を満たすことができます。

Lambda オーソライザー, Cognito オーソライザー

Lambda オーソライザーとは、Lambda 関数を利用したトークンによる認証機構です。
具体的には以下の通りに API Gateway と組み合わせることで認証機構を導入することができます。

  1. API のメソッド毎に別のトークンを割り振る
  2. API 実行時にトークンを渡す
  3. Lambda 関数が渡されたトークンとリクエストされたメソッドのトークンが一致しているかチェック
    → 一致していれば API が実行される
    → 一致していなければ権限エラーが返される

あらかじめ各ユーザーに固有のトークンを共有しておくことで、本人のフォルダ以外にはアクセスできなくなります。

Cognito オーソライザーも Lambda オーソライザーと認証の流れは同じです。
ただ、使用するサービスが Lambda 関数から Cognito に変わると認識していただければ問題ありません。

これらのオーソライザーの特長は送信元 IP といったユーザーの情報がなくともユーザー認証を行えることです。

一方で、リソースポリシーと比較して以下の様なデメリットがあるため、送信元 IP が分かっているのであればリソースポリシーを使用することを推奨します。

  • 料金が発生する
  • トークンの管理コストが発生する

API キー

API キーとは API へアクセスする際に要求される文字列のことです。

上記の説明だけではアクセス制御にちょうどいいと思われるかもしれませんが、API キーの特長は API のリクエストレートを制限できることです。

API は広く公開されていることもあるため、リクエスト回数が多くなりやすく、その分料金も多く発生します。
しかし、API キーを有効にすることでリクエストレートを制限し、料金を抑制することができます。

そのため、リクエストレートを同じ値に設定したい複数の API に同一の API キーが割り当てられるという使用法が一般的ですが、そのような使用法では本要件を満たすことができません。

もちろん、各 API に別々の API キーを割りあてればアクセス制御は可能ですが、そもそも API キーの使用上のベストプラクティスとして、API キーをアクセス制御の用途で使用することが推奨されていません。

API キーを使用した使用量プランの作成と使用 – Amazon API Gateway

素直にリソースポリシーかオーソライザーを使用しましょう。

<番外編> S3 のバケットポリシーではだめなの?

S3 へのアクセスを制御したい場合、真っ先に思い浮かぶ方法が S3 のバケットポリシーだと思います。
しかし、本アーキテクチャでは S3 のバケットポリシーは有効ではありません。

例えば、ユーザーのアクセスを送信元 IP アドレスで制御するケースを想定します。
この場合、バケットポリシーの Condition 要素で以下の様に記述することで、指定の IP アドレスのみ許可することが可能です。

{
	"Version": "2012-10-17",
	"Statement": {
		"Effect": "Allow",
		"Action": "someservice:*",
		"Resource": "*",
		"Condition": {
			"IpAddress": {
				"aws:SourceIp": ["203.0.113.0/24", "2001:DB8:1234:5678::/64"]
			}
		}
	}
}

しかし、このバケットポリシーが参照するアクセス元は、ユーザーではなく API Gateway になってしまいます。

従って、ユーザーのアクセスがどこから来たのかを判別することができません。

本ケースに限らず、何らかの AWS サービスを介して S3 にアクセスする場合、ユーザーの IP アドレスを S3 が参照することはできないのでご注意ください。

現在のところ、JSON ポリシーで評価を行うために、発信元サービスを通じて元の IP アドレスをターゲットサービスに渡す方法はありません。
これらのタイプのサービス API 呼び出しでは、aws:SourceIp 条件キーを使用しないでください。
出典:IAM JSON ポリシー要素: 条件演算子 – AWS Identity and Access Management

そこで、今回は API Gateway の機能として備わっているアクセス制御方法を利用しました。

まとめ

今回は API Gateway + S3 の構成でフォルダ毎のアクセス制御をしたい場合のアクセス制御方法をご紹介しました。
個人的には「S3 だしバケットポリシーで何とかなるだろう」という甘い目論見が通らなかったことが印象的でした。
同じような悩みを持つアナタに届けば幸いです。

ここまで読んでいただきありがとうございました!

henge

こんにちは。 第二SAチームのhengeです。 CIV6やHOI4みたいなストラテジーゲームが好きです。最近はたまにゴルフもします。 よろしくお願いします。

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら