コンテンツにスキップ

CT.LAMBDA.PV.2

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

コントロールの説明

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

本コントロールは SCP により、Lambda 関数に対する lambda:AddPermission API で外部プリンシパルを設定する操作をブロックします。パブリック(Principal: *)だけでなく、特定の外部アカウント ID を指定したクロスアカウントの add-permission もブロックされる。

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

検証環境

検証環境の構成図

本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。事前に export AWS_PROFILE=Workload でデフォルトを設定しておくと便利。

検証の流れ

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

結果

  • コントロールの有効化前、Lambda 関数のリソースベースポリシーに Principal: * を設定できることを確認できた。
  • Security Hub [Lambda.1] で「FAILED」として検出されることを確認できた。
  • コントロールの有効化後、既存のリソースベースポリシー(Principal: *)は維持されることを確認できた。SCP は API コールをブロックするだけで既存設定には遡及しないため、Security Hub [Lambda.1] も FAILED のまま変化しなかった。
  • 有効化中に別の関数で lambda:AddPermissionPrincipal: *)を試みると、explicit deny in a service control policy で拒否されることを確認できた。
  • 有効化中にクロスアカウント(--principal <組織外アカウント ID>)の add-permission も同様に拒否されることを確認できた。パブリックだけでなくクロスアカウントの設定もブロックされる。
  • 拒否されたイベントが 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 での検出確認(Lambda.1 FAILED)

Security Hub の検出はスケジュールベースのため、すぐに確認したい場合は Config ルールの手動トリガーが必要です。手順は補足2を参照してください。

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-<ハッシュ>

Security Hub で検出を確認する。

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Value": "Lambda.1", "Comparison": "EQUALS"}],
    "ComplianceStatus": [{"Value": "FAILED", "Comparison": "EQUALS"}],
    "ResourceId": [{"Value": "arn:aws:lambda:ap-northeast-1:<Workload のアカウント ID>:function:ct-lambda-pv2-test-1", "Comparison": "EQUALS"}]
  }' \
  --query 'Findings[0].{ComplianceStatus:Compliance.Status,ResourceId:Resources[0].Id}'
{
    "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> \
  --profile Master
{
    "enabledControls": []
}

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

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

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

aws controltower get-control-operation \
  --operation-identifier <オペレーション ID> \
  --query 'controlOperation.{operationType:operationType,status:status,statusMessage:statusMessage}' \
  --profile Master
{
    "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. Security Hub Lambda.1 が FAILED のまま変化しないことの確認

SCP は API コールをブロックするだけで既存のリソースベースポリシーには遡及しないため、Security Hub [Lambda.1] は FAILED のまま変化しない。

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

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

別の関数で 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:
arn:aws:organizations::<管理アカウント ID>:policy/<組織 ID>/service_control_policy/<ポリシー ID>

クロスアカウントの add-permission も拒否されることの確認

クロスアカウント(特定の外部アカウント ID)の add-permission も同様にブロックされることを確認する。

aws lambda add-permission \
  --function-name ct-lambda-pv2-test-2 \
  --statement-id crossaccount-access \
  --action lambda:InvokeFunction \
  --principal <組織外アカウント ID>
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:
arn:aws:organizations::<管理アカウント ID>:policy/<組織 ID>/service_control_policy/<ポリシー ID>

8. CloudTrail での確認

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

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

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AddPermission20150331v2 \
  --start-time <検索開始時刻> \
  --end-time <検索終了時刻> \
  --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 '<検索開始時刻>' +%s) \
  --end-time $(date -d '<検索終了時刻>' +%s) \
  --query-string 'fields @timestamp, eventName, errorCode, errorMessage
    | filter eventName like /AddPermission/ and errorCode like /AccessDenied/
    | sort @timestamp asc' \
  --profile Master

queryId が返るので、これを使って結果を取得する。

{
    "queryId": "<クエリ ID>"
}
aws logs get-query-results \
  --query-id <クエリ ID> \
  --query '{results:results,status:status}' \
  --profile Master
{
    "results": [
        [
            {"field": "@timestamp", "value": "<タイムスタンプ>"},
            {"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: arn:aws:organizations::<管理アカウント ID>:policy/<組織 ID>/service_control_policy/<ポリシー ID>"},
            {"field": "@ptr", "value": "<ログポインタ>"}
        ]
    ],
    "status": "Complete"
}

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

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

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

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

10. 制限解除の確認

コントロール無効化後、再び 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\"}"
}

11. クリーンアップ

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

# リソースベースポリシーを削除
aws lambda remove-permission --function-name ct-lambda-pv2-test-1 --statement-id public-access
aws lambda remove-permission --function-name ct-lambda-pv2-test-2 --statement-id public-access

# 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

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

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

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

なお、AuthType: NONE での関数 URL 作成自体をブロックしたい場合は、CT.LAMBDA.PV.1 を併用してください。

補足2: Config ルールの手動トリガーで Security Hub の検出を早める

Security Hub の検出はスケジュールベースのため、リソース作成直後には検出されないことがある。Config ルールを手動トリガーすることで、数十秒〜数分程度で検出を確認できる。

Lambda.1 に対応する Config ルール名を確認する。

aws configservice describe-config-rules \
  --query 'ConfigRules[?starts_with(ConfigRuleName, `securityhub-lambda-function-public-access-prohibited`)].ConfigRuleName'

Config ルールを手動トリガーする。

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

Config の評価結果を直接確認する(Security Hub への反映前に状態を把握できる)。

aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-lambda-function-public-access-prohibited-<ハッシュ> \
  --compliance-types NON_COMPLIANT \
  --query 'EvaluationResults[].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType}'

Amazonアソシエイトリンク