コンテンツにスキップ

CT.LAMBDA.PV.2

検証日: 2026-03-15 / リージョン: ap-northeast-1

コントロールの説明

Lambda 関数のリソースベースポリシーで、自アカウント外のプリンシパルへのアクセス許可を禁止します。

本コントロールは SCP により、Lambda 関数に対する lambda:AddPermission API で外部プリンシパル(Principal: * など)を設定する操作をブロックします。これにより、Lambda 関数がパブリックに公開されることを防ぎます。

Security Hub CSPM の [Lambda.1] は「Lambda 関数のリソースベースポリシーがパブリックアクセスを禁止しているか」をチェック(検出)しますが、本コントロールを有効にすることで、そもそもパブリックアクセスの設定自体を API レベルでブロックします。

検証の流れ

    flowchart LR
    A[1. 事前準備] --> B[2. Principal:* 設定成功]
    B --> C[3. Security Hub Lambda.1<br>で検出を確認]
    C --> D[4. CT.LAMBDA.PV.2<br>有効化]
    D --> E[5. 既存設定<br>維持を確認]
    D --> F[6. 新規 Principal:*<br>設定拒否を確認]
    E & F --> G[7. CloudTrail<br>で確認]
    G --> H[8. CT.LAMBDA.PV.2<br>無効化]
    H --> I[9. Principal:*<br>設定成功を確認]
  

結果

  • コントロールの有効化前、Lambda 関数のリソースベースポリシーに Principal: * を設定できることを確認できた。
  • Security Hub [Lambda.1] で「FAILED」として検出されることを確認できた。
  • コントロールの有効化後、既存のリソースベースポリシー(Principal: *)は維持されることを確認できた。
  • 有効化中に別の関数で lambda:AddPermissionPrincipal: *)を試みると、explicit deny in a service control policy で拒否されることを確認できた。
  • 拒否されたイベントが CloudTrail に記録されること、および管理アカウントの Organization Trail(CloudWatch Logs)からも確認できることを確認できた。
  • コントロールの無効化後、再び Principal: * の設定が可能になることを確認できた。

1. 事前準備

検証用の IAM 実行ロールと Lambda 関数を作成する。

aws iam create-role \
  --role-name ct-lambda-pv2-test-role \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {"Service": "lambda.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }]
  }' \
  --query 'Role.Arn'
"arn:aws:iam::<Workload のアカウント ID>:role/ct-lambda-pv2-test-role"

Lambda 関数を 2 つ作成する(有効化前テスト用と有効化後テスト用)。

echo 'def handler(event, context): return "ok"' > lambda_function.py
zip function.zip lambda_function.py
aws lambda create-function \
  --function-name ct-lambda-pv2-test-1 \
  --runtime python3.12 \
  --role arn:aws:iam::<Workload のアカウント ID>:role/ct-lambda-pv2-test-role \
  --handler lambda_function.handler \
  --zip-file fileb://function.zip \
  --query '{FunctionName:FunctionName,FunctionArn:FunctionArn}'
{
    "FunctionName": "ct-lambda-pv2-test-1",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-1"
}
aws lambda create-function \
  --function-name ct-lambda-pv2-test-2 \
  --runtime python3.12 \
  --role arn:aws:iam::<Workload のアカウント ID>:role/ct-lambda-pv2-test-role \
  --handler lambda_function.handler \
  --zip-file fileb://function.zip \
  --query '{FunctionName:FunctionName,FunctionArn:FunctionArn}'
{
    "FunctionName": "ct-lambda-pv2-test-2",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-2"
}

2. コントロール有効化前の確認

リソースベースポリシーに Principal: * を追加する。

aws lambda add-permission \
  --function-name ct-lambda-pv2-test-1 \
  --statement-id public-access \
  --action lambda:InvokeFunction \
  --principal '*'
{
    "Statement": "{\"Sid\":\"public-access\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-1\"}"
}

ポリシーを確認する。

aws lambda get-policy --function-name ct-lambda-pv2-test-1 \
  --output json | jq '.Policy | fromjson'
{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "public-access",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-1"
    }
  ]
}

3. Security Hub CSPM での検出確認

Config ルールを手動トリガーして検出を早める。Lambda.1 に対応する Config ルール名は securityhub-lambda-function-public-access-prohibited-<ハッシュ> の形式で、環境ごとにハッシュ部分が異なる。

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-lambda-function-public-access-prohibited-<ハッシュ>

Audit アカウントの Security Hub で検出を確認する。

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Value": "Lambda.1", "Comparison": "EQUALS"}],
    "ComplianceStatus": [{"Value": "FAILED", "Comparison": "EQUALS"}]
  }' \
  --profile Audit
{
    "ComplianceStatus": "FAILED",
    "ResourceId": "arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-1"
}

4. 対象の予防コントロールを有効化(例外設定なし)

Control Tower の管理アカウントで、対象の OU にコントロールが有効になっていないことを確認する。

aws controltower list-enabled-controls \
  --target-identifier <OU の ARN>
{
    "enabledControls": []
}

コントロールを有効化する。

aws controltower enable-control \
  --control-identifier arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy \
  --target-identifier <OU の ARN>
{
    "arn": "arn:aws:controltower:ap-northeast-1:<管理アカウント ID>:enabledcontrol/<enabledcontrol ID>",
    "operationIdentifier": "<オペレーション ID>"
}

有効化が完了するまで待機する。

aws controltower get-control-operation \
  --operation-identifier <オペレーション ID>
{
    "controlOperation": {
        "operationType": "ENABLE_CONTROL",
        "status": "SUCCEEDED",
        "statusMessage": "Operation was successful."
    }
}

5. 既存設定は維持されることの確認

有効化前に設定したリソースベースポリシーが維持されていることを確認する。

aws lambda get-policy --function-name ct-lambda-pv2-test-1 \
  --output json | jq '.Policy | fromjson'
{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "public-access",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-1"
    }
  ]
}

有効化前に設定した Principal: * のリソースベースポリシーは、コントロール有効化後も維持される。

6. 新規操作は制限(拒否)されることの確認

別の関数で Principal: * を設定する。

aws lambda add-permission \
  --function-name ct-lambda-pv2-test-2 \
  --statement-id public-access \
  --action lambda:InvokeFunction \
  --principal '*'
An error occurred (AccessDeniedException) when calling the AddPermission operation:
User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<セッション名>
is not authorized to perform: lambda:AddPermission on resource:
arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-2
with an explicit deny in a service control policy

7. CloudTrail での確認

拒否されたイベントが CloudTrail に記録されていることを確認する。

Workload アカウントの CloudTrail イベント履歴から確認する。Lambda の AddPermission は CloudTrail 上では AddPermission20150331v2 というイベント名で記録される。

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AddPermission20150331v2 \
  --start-time 2026-03-15T17:00:00+09:00 \
  --end-time 2026-03-15T17:15:00+09:00 \
  --query 'Events[].CloudTrailEvent' \
  --output text | jq 'select(.errorCode != null) | {eventName, errorCode, errorMessage}'
{
  "eventName": "AddPermission20150331v2",
  "errorCode": "AccessDenied",
  "errorMessage": "User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<セッション名> is not authorized to perform: lambda:AddPermission on resource: arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-2 with an explicit deny in a service control policy: arn:aws:organizations::<管理アカウント ID>:policy/<組織 ID>/service_control_policy/<ポリシー ID>"
}

errorMessageexplicit deny in a service control policy と記録されており、SCP による拒否であることがわかる。なお、EC2 系の SCP 拒否では errorCodeClient.UnauthorizedOperation だったが、Lambda では AccessDenied となる。

管理アカウントの Organization Trail(CloudWatch Logs)からも同じイベントを確認できる。

aws logs start-query \
  --log-group-name "aws-controltower/CloudTrailLogs" \
  --start-time $(date -d '2026-03-15T17:00:00+09:00' +%s) \
  --end-time $(date -d '2026-03-15T17:15:00+09:00' +%s) \
  --query-string 'fields @timestamp, eventName, errorCode, errorMessage
    | filter eventName like /AddPermission/ and errorCode like /AccessDenied/
    | sort @timestamp asc'
aws logs get-query-results --query-id <クエリ ID>
{
    "results": [
        [
            {"field": "@timestamp", "value": "2026-03-15 08:08:10.438"},
            {"field": "eventName", "value": "AddPermission20150331v2"},
            {"field": "errorCode", "value": "AccessDenied"},
            {"field": "errorMessage", "value": "User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<セッション名> is not authorized to perform: lambda:AddPermission on resource: arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-2 with an explicit deny in a service control policy"}
        ]
    ],
    "status": "Complete"
}

管理アカウントの CloudWatch Logs にも同じ拒否イベントが記録されていることを確認できた。

8. 対象の予防コントロールを無効化

検証完了後、コントロールを無効化する。

aws controltower disable-control \
  --control-identifier arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy \
  --target-identifier <OU の ARN>
{
    "operationIdentifier": "<オペレーション ID>"
}
aws controltower get-control-operation \
  --operation-identifier <オペレーション ID>
{
    "controlOperation": {
        "operationType": "DISABLE_CONTROL",
        "status": "SUCCEEDED",
        "statusMessage": "Operation was successful."
    }
}

9. 制限解除の確認

コントロール無効化後、再び Principal: * の設定が可能になることを確認する。

aws lambda add-permission \
  --function-name ct-lambda-pv2-test-2 \
  --statement-id public-access \
  --action lambda:InvokeFunction \
  --principal '*'
{
    "Statement": "{\"Sid\":\"public-access\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-2\"}"
}

10. クリーンアップ

検証で作成したリソースをすべて削除する。

# Lambda関数を削除
aws lambda delete-function --function-name ct-lambda-pv2-test-1
aws lambda delete-function --function-name ct-lambda-pv2-test-2

# IAMロールを削除
aws iam delete-role --role-name ct-lambda-pv2-test-role

補足: 関数 URL のパブリック公開について

Lambda には関数 URL という機能があり、認証タイプを NONE に設定すると、URL を知っていれば誰でもアクセスできる状態になります。

ただし、実際にパブリックアクセスを有効にするには、lambda:AddPermissionPrincipal: * のリソースベースポリシーを追加する必要があります。本コントロール有効化中に CreateFunctionUrlConfig(認証タイプ NONE)を実行したところ、関数 URL 自体は作成できましたが、リソースベースポリシーは自動追加されず、パブリックアクセスは 403 で拒否されました。AddPermissionPrincipal: * を追加しようとするとコントロールにブロックされるため、関数 URL のパブリック公開も間接的に防ぐことができます。

Amazonアソシエイトリンク