CT.LAMBDA.PV.2
コントロールの説明
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:AddPermission(Principal: *)を試みると、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.pyaws 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 policy7. 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>"
}errorMessage に explicit deny in a service control policy と記録されており、SCP による拒否であることがわかる。なお、EC2 系の SCP 拒否では errorCode が Client.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:AddPermission で Principal: * のリソースベースポリシーを追加する必要があります。本コントロール有効化中に CreateFunctionUrlConfig(認証タイプ NONE)を実行したところ、関数 URL 自体は作成できましたが、リソースベースポリシーは自動追加されず、パブリックアクセスは 403 で拒否されました。AddPermission で Principal: * を追加しようとするとコントロールにブロックされるため、関数 URL のパブリック公開も間接的に防ぐことができます。