Lambda関数がパブリックアクセス可能である場合のプライベート設定化の手順

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

ポリシーの説明
[Lambda.1] Lambda 関数ポリシーでは、パブリックアクセスを禁止する必要があります
Lambda の Security Hub ブコントロール – AWS Security Hub
このコントロールは、Lambda 関数リソースベースポリシーがアカウントの外部からのパブリックアクセスを禁止しているかどうかをチェックします。パブリックアクセスが許可されている場合、コントロールは失敗します。Lambda 関数が Amazon S3 から呼び出され、ポリシーが
AWS:SourceAccount
などパブリックアクセスを制限する条件が含まれていない場合も、コントロールは失敗します。より細かくアクセスするには、バケットポリシーで他の S3 条件をAWS:SourceAccount
と併用することをおすすめします。
このポリシーは、Lambda関数リソースベースポリシーがアカウント外部からのパブリックアクセスを禁止していることを求めています。Lambda関数に意図しないパブリックアクセスを許可すると、不正なコード実行、データ処理、あるいは過剰な課金につながるリスクが高まります。特にS3からの呼び出しを許可する場合、適切な条件を設定し、アクセスを厳密に制限する必要があります。
修復方法
AWSコンソールでの修正手順
- AWSマネジメントコンソールにログインし、Lambdaコンソールを開きます。
- 修正対象の関数を選択して開き、「設定」タブ > 「アクセス権限」メニューを選択します。
- 「リソースベースのポリシー」セクションで、パブリックアクセスを許可している可能性のあるポリシーステートメント(Statement ID)をクリックします。(または、「ポリシーを追加」で新規に制限されたポリシーを作成します。)

- ポリシーエディタ(または権限追加画面)で、
Principal
を特定のAWSサービス(例:s3.amazonaws.com
,apigateway.amazonaws.com
)や信頼できるAWSアカウントIDに限定します。アスタリスク () の使用は避けます。 - 必要に応じて
Condition
ブロックを追加(または編集)し、AWS:SourceAccount
(呼び出し元AWSアカウントID)、AWS:SourceArn
(呼び出し元リソースのARN)、AWS:PrincipalOrgID
(呼び出し元が属するAWS Organization ID) などの条件キーを使用して、アクセス元をさらに厳密に制限します。 - 「保存」をクリックし設定を反映します。
Terraformでの修復手順
以下は、セキュアな設定(VPC、最小権限IAMロール、Secrets Manager連携、コード署名など)を持ち、かつ非パブリックなリソースベースポリシーを持つ新しいLambda関数をTerraformで作成するコード例です。
# Lambda関数の基本設定
resource "aws_lambda_function" "secure_function" {
filename = var.lambda_package_path
function_name = "${var.environment}-${var.function_name}"
role = aws_iam_role.lambda_execution_role.arn
handler = var.handler
runtime = var.runtime
timeout = var.timeout
memory_size = var.memory_size
# 環境変数の暗号化
environment {
variables = {
Environment = var.environment
# 機密情報はSecretsManagerから取得
DB_CONNECTION = aws_secretsmanager_secret.lambda_secrets.arn
}
}
# VPC内で実行(推奨)
vpc_config {
subnet_ids = var.subnet_ids
security_group_ids = [aws_security_group.lambda_sg.id]
}
# コード署名の設定
code_signing_config_arn = aws_lambda_code_signing_config.signing_config.arn
tags = {
Environment = var.environment
Service = var.function_name
}
}
# コード署名の設定
resource "aws_lambda_code_signing_config" "signing_config" {
allowed_publishers {
signing_profile_version_arns = [aws_signer_signing_profile.lambda_profile.arn]
}
policies {
untrusted_artifact_on_deployment = "Enforce"
}
}
# 実行ロール
resource "aws_iam_role" "lambda_execution_role" {
name = "${var.environment}-${var.function_name}-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
# 基本的な実行ポリシー
resource "aws_iam_role_policy_attachment" "lambda_basic" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
role = aws_iam_role.lambda_execution_role.name
}
# VPCアクセス用ポリシー
resource "aws_iam_role_policy_attachment" "lambda_vpc" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
role = aws_iam_role.lambda_execution_role.name
}
# カスタムポリシー(最小権限の原則に従う)
resource "aws_iam_role_policy" "lambda_custom" {
name = "${var.environment}-${var.function_name}-custom-policy"
role = aws_iam_role.lambda_execution_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue"
]
Resource = [aws_secretsmanager_secret.lambda_secrets.arn]
}
]
})
}
# セキュアなリソースベースポリシー
resource "aws_lambda_permission" "secure_invoke" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.secure_function.function_name
principal = "apigateway.amazonaws.com"
# 特定のAPIゲートウェイのみからの呼び出しを許可
source_arn = "${var.api_gateway_arn}/*/*/*"
# プリンシパルタグによる制限(オプション)
principal_org_id = var.aws_organization_id
}
# セキュリティグループ
resource "aws_security_group" "lambda_sg" {
name = "${var.environment}-${var.function_name}-sg"
description = "Security group for Lambda function"
vpc_id = var.vpc_id
# 必要最小限のアウトバウンドルール
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.environment}-${var.function_name}-sg"
Environment = var.environment
}
}
# Secrets Managerでの機密情報管理
resource "aws_secretsmanager_secret" "lambda_secrets" {
name = "${var.environment}/${var.function_name}/secrets"
tags = {
Environment = var.environment
Service = var.function_name
}
}
resource "aws_secretsmanager_secret_version" "lambda_secrets" {
secret_id = aws_secretsmanager_secret.lambda_secrets.id
secret_string = jsonencode({
DB_CONNECTION = var.database_connection_string
API_KEY = var.api_key
})
}
# CloudWatch Logsグループ
resource "aws_cloudwatch_log_group" "lambda_logs" {
name = "/aws/lambda/${aws_lambda_function.secure_function.function_name}"
retention_in_days = var.log_retention_days
tags = {
Environment = var.environment
Service = var.function_name
}
}
# 必要な変数定義
variable "environment" {
type = string
description = "環境名(例: prod, staging, dev)"
}
variable "function_name" {
type = string
description = "Lambda関数名"
}
variable "lambda_package_path" {
type = string
description = "Lambda関数のパッケージパス"
}
variable "handler" {
type = string
description = "Lambda関数のハンドラ"
}
variable "runtime" {
type = string
description = "Lambda関数のランタイム"
}
variable "vpc_id" {
type = string
description = "VPC ID"
}
variable "subnet_ids" {
type = list(string)
description = "サブネットIDのリスト"
}
variable "api_gateway_arn" {
type = string
description = "API GatewayのARN"
}
variable "aws_organization_id" {
type = string
description = "AWS Organization ID"
default = null
}
# 出力
output "function_arn" {
value = aws_lambda_function.secure_function.arn
}
output "function_name" {
value = aws_lambda_function.secure_function.function_name
}
重要な修正ポイントと実装手順を説明します:
- セキュアなリソースベースポリシー(最重要):
aws_lambda_permission
リソース (secure_invoke
ブロック) が、Lambda関数へのアクセス許可(リソースベースポリシーの一部)を定義します。ここでパブリックアクセスを許可しないように設定することが重要です。
resource "aws_lambda_permission" "secure_invoke" {
statement_id = "AllowAPIGatewayInvoke" # わかりやすいID
action = "lambda:InvokeFunction" # 実行権限
function_name = aws_lambda_function.secure_function.function_name # 対象関数
principal = "apigateway.amazonaws.com" # ★呼び出し元を特定サービスに限定
# ★呼び出し元リソースを特定のAPI Gateway ARNに限定
source_arn = "${var.api_gateway_arn}/*/*/*"
# ★(オプション) 呼び出し元が特定のAWS Organizationに属する場合のみ許可
principal_org_id = var.aws_organization_id
}
- この例では、
principal
で呼び出し元サービスをAPI Gatewayに限定し、source_arn
で特定のAPI Gatewayリソースからの呼び出しのみを許可しています。さらにprincipal_org_id
で組織による制限も可能です。これにより、不特定多数からのアクセスを防ぎます。S3トリガーの場合は、principal
をs3.amazonaws.com
にし、source_account
やsource_arn
でバケットを限定します。 - 最小権限の実行ロール:
aws_iam_role
(lambda_execution_role
) とaws_iam_role_policy
(lambda_custom
) で、Lambda関数が必要とする最小限の権限(ここではSecrets Managerへのアクセスのみ)を付与しています。
- その他のセキュリティベストプラクティス:
- VPC内実行 (
vpc_config
) によるネットワーク分離。 - Secrets Manager (
aws_secretsmanager_secret
) による機密情報の安全な管理。 - コード署名 (
aws_lambda_code_signing_config
) によるデプロイされるコードの信頼性確保。
- VPC内実行 (
注意:
- このTerraformコードは新規にセキュアなLambda関数と関連リソースを作成する例です。既存のTerraform管理下の関数のリソースベースポリシーを変更する場合は、該当の
aws_lambda_permission
リソース定義を修正(または新規追加)し、terraform apply
を実行します。 - Terraform管理外の既存関数のポリシーを修正する場合は、コンソールまたはAWS CLIでの修正が基本となります。
最後に
今回は、Lambda関数リソースベースポリシーのパブリックアクセス設定について解説しました。パブリックアクセスを許可すると、不正アクセスや意図しない実行のリスクが高まります。必ずリソースベースポリシーでアクセス元を厳密に制限し、パブリックアクセスを禁止してください。必要な場合でも、Condition
を使用してアクセス元を可能な限り限定することが重要です。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。