Amazon API Gateway V2 ステージのアクセスロギングの設定について

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。

今回は、API Gateway V2 (HTTP/WebSocket API) ステージで アクセスロギングが設定されていない 状態について、そのリスクと対策を解説します。

ポリシーの説明

Amazon API Gateway の Security Hub コントロール – AWS Security Hub

[APIGateway.9] API Gateway V2 ステージにアクセスロギングを設定する必要があります

このコントロールは、Amazon API Gateway V2 ステージのアクセスログが設定されているかどうかをチェックします。アクセスログ設定が定義されていない場合、このコントロールは失敗します。

リスク

Amazon API Gateway V2は、HTTP APIやWebSocket APIを構築するためのサービスであり、REST API (V1) よりも低コストで高性能な選択肢として利用が広がっています。しかし、API Gateway V2ステージでアクセスロギングが有効になっていない場合、以下のようなセキュリティおよび運用上のリスクが発生します。

  • セキュリティインシデントの検知と調査の困難さ: アクセスログは、誰がAPIにアクセスしたか、いつアクセスしたか、どのIPアドレスからアクセスしたか、どのようなリクエストが行われたか、どのようなレスポンスが返されたかなど、APIの使用状況に関する詳細な情報を提供します。ログがなければ、不正なアクセス試行、データ漏洩、DDoS攻撃の兆候などを早期に検知することが困難になります。また、セキュリティインシデント発生時のフォレンジック調査や根本原因の特定が極めて難しくなります。
  • トラフィックパターンの分析と最適化の欠如: アクセスログは、APIの利用状況、人気のあるエンドポイント、エラー率、レイテンシーなどのトラフィックパターンを分析するための貴重なデータを提供します。ログがなければ、APIのパフォーマンス最適化、スケーリング計画、ユーザー行動の理解が困難になります。
  • トラブルシューティングの困難さ: APIの呼び出しに関する問題(例: エラー、遅延、不正なレスポンス)が発生した場合、アクセスログは問題の診断と解決に不可欠な情報源となります。ログがなければ、問題の再現や原因の特定に大幅な時間がかかり、サービスの可用性に影響を与える可能性があります。
  • 不正行為の特定不可: APIが不正利用されたり、悪意のあるボットによってスクレイピングされたりした場合、アクセスログがなければ、これらの不正行為を特定し、対策を講じることができません。

対策

API Gateway V2ステージにアクセスロギングを設定することは、APIのセキュリティ監査、トラブルシューティング、トラフィック分析能力を大幅に向上させるための重要なベストプラクティスです。

  • ログの送信先を選択: API Gateway V2のアクセスログは、Amazon CloudWatch Logs または Amazon S3 に送信できます。
    • CloudWatch Logs: リアルタイム監視、アラート、ログ分析(CloudWatch Logs Insights)に適しています。
    • S3: 長期保存、コスト効率、大規模なデータ分析(Athena、Glueなど)に適しています。セキュリティと長期保存の観点から、S3への発行も強力に推奨されます。
  • ログ形式の選択: アクセスログの形式を定義できます。JSON形式やカスタム形式など、必要な情報(リクエストID、HTTPメソッド、パス、ステータスコード、レイテンシー、クライアントIPなど)が含まれるように設定します。
  • IAMロールの適切な権限: API GatewayがログをCloudWatch LogsまたはS3に書き込むために必要な権限を持つIAMロールを割り当てる必要があります。
  • ログの保存期間とライフサイクル: CloudWatch Logsを使用する場合は、ロググループの保持期間を設定します。S3を使用する場合は、S3ライフサイクルポリシーを設定して、ログの保存期間とアーカイブ/削除を管理します。組織のコンプライアンス要件に合わせて設定してください。
  • ログの分析と監視: アクセスログを収集したら、それを活用してセキュリティ監視と運用効率を向上させます。
    • CloudWatch Logsに発行する場合: CloudWatch Logs Insightsでクエリを実行して特定のイベントを検索したり、メトリクスフィルターとアラームを設定して異常なトラフィックパターン(例: 4xx/5xxエラーの急増、特定のIPからの異常なリクエスト数)を検知し、通知を受け取ることができます。
    • S3に発行する場合: Amazon AthenaとAmazon QuickSightを使用して、ログを分析・可視化します。これにより、APIの利用状況、パフォーマンス、セキュリティイベントを詳細に把握できます。

修復方法

AWSコンソールでの修復手順

AWSコンソールを使用して、API Gateway V2ステージにアクセスロギングを設定します。

前提:

  • アクセスロギングを設定したいAPI Gateway V2 (HTTP APIまたはWebSocket API) とステージが既に存在していること。
  • ログを保存するCloudWatch LogsグループまたはS3バケットが既に存在していること。
  • API Gatewayがログを書き込むための適切な権限を持つIAMロールが既に存在していること。
  1. API Gatewayサービスへ移動: AWSコンソールにログインし、API Gateway サービスを開きます。
  2. APIの選択: 左側のナビゲーションペインで「API」を選択し、アクセスロギングを設定したいAPIを選択します。
  3. ステージの選択: 左側のナビゲーションペインで「ステージ」を選択し、アクセスロギングを設定したいステージ(例: prod, dev など)を選択します。
  4. ロギング/モニタリングの設定: ステージの詳細画面で、「ログとトレース 」を見つけます。
  1. アクセスログの設定:カスタムのアクセスログ」を有効にします。
    • 送信先:
      • CloudWatch Logs」を選択する場合:
        • ロググループ: ログを送信するCloudWatch Logsグループを選択します。
        • IAM ロール: API Gatewayがログを書き込むためのIAMロールを選択します。通常、AWSServiceRoleForAPIGateway などのサービスリンクされたロールが推奨されます。
      • S3 バケット」を選択する場合:
        • S3 バケット: ログを送信するS3バケットを選択します。
        • IAM ロール: API Gatewayがログを書き込むためのIAMロールを選択します。
    • ログ形式: ドロップダウンメニューから、ログの形式を選択します。推奨されるのはJSON形式です。
      • 例: $context.requestId $context.requestTime $context.httpMethod $context.resourcePath $context.status $context.protocol $context.responseLength $context.integrationLatency $context.integrationStatus $context.error.message
      • またはJSON形式で:
{
    "requestId":"$context.requestId",
    "ip": "$context.identity.sourceIp",
    "caller":"$context.identity.caller",
    "user":"$context.identity.user",
    "requestTime":"$context.requestTime",
    "httpMethod":"$context.httpMethod",
    "resourcePath":"$context.resourcePath",
    "status":"$context.status",
    "protocol":"$context.protocol",
    "responseLength":"$context.responseLength",
    "integrationLatency":"$context.integrationLatency",
    "integrationStatus":"$context.integrationStatus",
    "errorMessage":"$context.error.message"
}
  1. 変更を保存: 設定を確認し、「変更を保存」をクリックします。

Terraformでの修復手順

TerraformでAPI Gateway V2ステージにアクセスロギングを設定するには、aws_apigatewayv2_stage リソースの access_log_settings ブロックを設定します。

前提:

  • API Gateway V2 APIとステージが既にTerraformで定義されているか、データソースとして参照されていること。
  • ログを保存するCloudWatch LogsグループまたはS3バケットが既にTerraformで定義されているか、データソースとして参照されていること。
  • API Gatewayがログを書き込むための適切な権限を持つIAMロールが既に定義されているか、データソースとして参照されていること。
# (例) 既存のAPI Gateway V2 HTTP APIとステージを参照
data "aws_apigatewayv2_api" "my_http_api" {
  name = "MyExampleHttpApi" # あなたのHTTP API名に置き換え
}

data "aws_apigatewayv2_stage" "my_http_api_stage" {
  api_id    = data.aws_apigatewayv2_api.my_http_api.id
  stage_name = "prod" # あなたのステージ名に置き換え
}

# (例) アクセスログの送信先となるCloudWatch Logsグループ
resource "aws_cloudwatch_log_group" "api_gateway_access_logs" {
  name              = "/aws/apigateway/my-http-api-access-logs" # ロググループ名
  retention_in_days = 30 # ログの保持期間 (日数)

  tags = {
    Environment = "Production"
  }
}

# (例) API GatewayがCloudWatch Logsにログを書き込むためのIAMロール
# 通常、サービスリンクされたロール (AWSServiceRoleForAPIGateway) が自動的に作成・使用されます。
# 明示的にロールを定義する場合は、以下の例を参考にしてください。
resource "aws_iam_role" "api_gateway_cloudwatch_logs_role" {
  name = "ApiGatewayCloudWatchLogsRole"

  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Effect = "Allow",
        Principal = {
          Service = "apigateway.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy" "api_gateway_cloudwatch_logs_policy" {
  name = "ApiGatewayCloudWatchLogsPolicy"
  role = aws_iam_role.api_gateway_cloudwatch_logs_role.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ],
        Effect = "Allow",
        Resource = "${aws_cloudwatch_log_group.api_gateway_access_logs.arn}:*"
      }
    ]
  })
}

# API Gateway V2ステージのアクセスロギング設定を更新
resource "aws_apigatewayv2_stage" "my_http_api_stage_with_logging" {
  # 既存のステージを参照し、設定を更新する場合
  # data.aws_apigatewayv2_stage.my_http_api_stage.id を直接更新することはできないため、
  # 既存のステージを管理下に置き、アクセスログ設定を追加する形になります。
  # または、新しいステージを作成してアクセスログを設定します。
  # ここでは、既存ステージの更新を想定し、既存ステージの情報を再定義する形で示します。

  api_id      = data.aws_apigatewayv2_api.my_http_api.id
  name        = data.aws_apigatewayv2_stage.my_http_api_stage.name # 既存ステージ名
  auto_deploy = true # デプロイを自動化する場合

  # アクセスログ設定
  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_gateway_access_logs.arn # CloudWatch LogsグループのARN
    # destination_arn = aws_s3_bucket.access_log_bucket.arn # S3バケットを宛先にする場合

    # ログ形式 (JSON形式を推奨)
    format = jsonencode({
        "requestId": "$context.requestId",
        "ip": "$context.identity.sourceIp",
        "caller": "$context.identity.caller",
        "user": "$context.identity.user",
        "requestTime": "$context.requestTime",
        "httpMethod": "$context.httpMethod",
        "resourcePath": "$context.resourcePath",
        "status": "$context.status",
        "protocol": "$context.protocol",
        "responseLength": "$context.responseLength",
        "integrationLatency": "$context.integrationLatency",
        "integrationStatus": "$context.integrationStatus",
        "errorMessage": "$context.error.message"
    })
  }

  # IAMロール ARNは、CloudWatch Logsに送信する場合のみ必要
  # S3に送信する場合は、S3バケットポリシーでアクセスを許可するため不要です。
  # cloudwatch_log_group_arn = aws_cloudwatch_log_group.api_gateway_access_logs.arn # CloudWatch Logsに送信する場合のみ
  # credentials_arn = aws_iam_role.api_gateway_cloudwatch_logs_role.arn # IAMロールのARNを指定

  tags = {
    Environment = "Production"
  }
}

# (S3にログを送信する場合の例)
/*
resource "aws_s3_bucket" "access_log_bucket" {
  bucket = "my-api-gateway-v2-access-logs-unique-12345" # グローバルで一意の名前に変更
  force_destroy = true # テスト用。本番環境ではfalseを推奨

  tags = {
    Name = "APIGatewayV2AccessLogBucket"
  }
}

resource "aws_s3_bucket_policy" "access_log_bucket_policy" {
  bucket = aws_s3_bucket.access_log_bucket.id
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Sid       = "APIGatewayS3AccessLogs"
        Effect    = "Allow"
        Principal = {
          Service = "apigateway.amazonaws.com"
        },
        Action    = "s3:PutObject",
        Resource  = "${aws_s3_bucket.access_log_bucket.arn}/*"
      }
    ]
  })
}
*/

上記のTerraformコードでは、aws_apigatewayv2_stage リソースの access_log_settings ブロックを使用して、API Gateway V2ステージにアクセスロギングを設定しています。

  • destination_arn: ログの送信先となるCloudWatch LogsグループまたはS3バケットのARNを指定します。
  • format: ログの形式をJSONで定義します。必要な情報を $context 変数を使用して含めます。
  • credentials_arn: CloudWatch Logsにログを送信する場合、API Gatewayがログを書き込むためのIAMロールのARNを指定します。S3に送信する場合は、S3バケットポリシーでアクセスを許可するため、通常は不要です。

MyExampleHttpApiprod/aws/apigateway/my-http-api-access-logs などのプレースホルダーは、実際の環境に合わせて修正してください。

最後に

この記事では、API Gateway V2ステージにアクセスロギングを設定することの重要性について解説しました。アクセスログは、APIのセキュリティ監査、トラブルシューティング、トラフィック分析に不可欠な情報源であり、これを有効にすることは、APIを運用する上での基本的なセキュリティ対策です。

貴社のAPI Gateway V2ステージでは、アクセスロギングが適切に設定されていますか?この機会にぜひ設定を確認・強化してみてください。

こちらの内容の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。運用が非常にラクに出来る製品になっていますのでぜひ興味がある方はお問い合わせお待ちしております。

最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る