コンテンツにスキップ

Lambda 関数

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

概要

IAM Access Analyzer が Lambda 関数の外部アクセスをどのように検出するかを検証する。

Lambda 関数はリソースベースポリシー(add-permission)により外部プリンシパルへのアクセスを許可できる。デフォルトではリソースベースポリシーが存在せず、明示的に追加しない限り外部アクセスは発生しない。リソースベースポリシーに Principal: "*" を設定するとパブリックアクセスとして検出され、特定の外部アカウントを設定するとクロスアカウントアクセスとして検出される。

Lambda 関数の外部アクセス制御レイヤー

レイヤーLambda 関数の状況
デフォルト保護なし(リソースベースポリシーがデフォルトで空)
予防(SCP)CT.LAMBDA.PV.2add-permission で外部プリンシパルの設定をブロック)
Security Hub CSPMLambda.1(パブリックアクセスのチェック)
IAM Access Analyzerパブリック + クロスアカウントの到達可能性を分析
CT.LAMBDA.PV.2 は SCP 型であり、他の記事で検証した RCP 型(CT.S3.PV.4、CT.KMS.PV.7、CT.STS.PV.1)とは動作が異なる。RCP はリソースへのアクセス自体をブロックするため、既存のポリシーが残っていてもアクセスは拒否される。一方、SCP はパブリック・クロスアカウント両方の add-permission API 呼び出しをブロックするが、既に設定されたリソースベースポリシーには影響しない。そのため、SCP を有効化しても、既に外部アクセスが設定された関数への Invoke は引き続き成功する。IAM Access Analyzer の resourceControlPolicyRestrictionNOT_APPLICABLE のまま変わらない。

結果

  • デフォルトのリソースベースポリシー: Lambda 関数はデフォルトではリソースベースポリシーが存在しない。add-permission で明示的に追加しない限り外部アクセスは発生しない
  • パブリックアクセスの検出: リソースベースポリシーに Principal: "*" を設定すると、isPublic: true の finding が生成された。action は lambda:InvokeFunction
  • クロスアカウントアクセスの検出: 組織外アカウントをリソースベースポリシーに設定すると、isPublic: false の finding が生成された。principal に組織外アカウント ID が特定された
  • 予防コントロール(CT.LAMBDA.PV.2)との連動: CT.LAMBDA.PV.2 は SCP 型であり、RCP 型(S3・KMS・IAM ロール)とは動作が異なる。SCP 有効時でも既存のパブリック関数への Invoke はブロックされず、resourceControlPolicyRestrictionNOT_APPLICABLE のまま変わらない。finding ID も変わらない。SCP は新規の add-permission API 呼び出しのみをブロックする
  • クリーンアップ: remove-permission でリソースベースポリシーを削除すると finding は RESOLVED になる

検証環境

検証環境の構成図

本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。

検証の流れ

    flowchart LR
    A[1. 既存 finding の<br>確認] --> B[2. テスト用関数<br>作成]
    B --> C[3. パブリックアクセス<br>検出の確認]
    C --> D[4. クロスアカウント<br>アクセス検出]
    D --> E[5. 予防コントロール<br>との連動]
    E --> F[6. クリーンアップ]
  

1. 既存 finding の確認

テスト用関数を作成する前に、既存の Lambda 関数 finding を確認する。

デフォルトのリソースベースポリシーの確認

Lambda 関数はデフォルトではリソースベースポリシーが存在しない。

aws lambda get-policy \
  --function-name <任意の既存関数名>
An error occurred (ResourceNotFoundException) when calling the GetPolicy operation: The resource you requested does not exist.

リソースベースポリシーが存在しないため、add-permission で明示的に追加しない限り外部アクセスは発生しない。

既存 finding の確認

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::Lambda::Function"]}}' \
  --query 'findings[].{id:id,resource:resource,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[]

2. テスト用関数の作成

外部プリンシパルへのアクセスを許可する関数を作成する。検証目的のみで行い、ステップ 6 で削除する。

2 つのテスト用 Lambda 関数を作成する。関数のコードは最小限のダミーとし、リソースベースポリシーで外部アクセスを設定する。

Lambda 実行ロールの作成

aws iam create-role \
  --role-name iaa-lambda-test-role \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {"Service": "lambda.amazonaws.com"},
        "Action": "sts:AssumeRole"
      }
    ]
  }'

関数コードの準備

echo 'def handler(event, context): return {"statusCode": 200, "body": "ok"}' > /tmp/lambda_function.py
cd /tmp && zip function.zip lambda_function.py

パブリックアクセス検証用関数

aws lambda create-function \
  --function-name iaa-lambda-public-test \
  --runtime python3.12 \
  --handler lambda_function.handler \
  --role arn:aws:iam::<Workload アカウント ID>:role/iaa-lambda-test-role \
  --zip-file fileb:///tmp/function.zip
{
    "FunctionName": "iaa-lambda-public-test",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test",
    "State": "Pending"
}

リソースベースポリシーに Principal: "*" を設定する。

aws lambda add-permission \
  --function-name iaa-lambda-public-test \
  --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:iaa-lambda-public-test\"}"
}

クロスアカウントアクセス検証用関数

aws lambda create-function \
  --function-name iaa-lambda-crossaccount-test \
  --runtime python3.12 \
  --handler lambda_function.handler \
  --role arn:aws:iam::<Workload アカウント ID>:role/iaa-lambda-test-role \
  --zip-file fileb:///tmp/function.zip
{
    "FunctionName": "iaa-lambda-crossaccount-test",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test",
    "State": "Pending"
}

リソースベースポリシーに組織外アカウントを設定する。

aws lambda add-permission \
  --function-name iaa-lambda-crossaccount-test \
  --statement-id crossaccount-access \
  --action lambda:InvokeFunction \
  --principal <組織外アカウント ID>
{
    "Statement": "{\"Sid\":\"crossaccount-access\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::<組織外アカウント ID>:root\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test\"}"
}

3. パブリックアクセスの検出

ポリシー変更後、IAM Access Analyzer が finding を生成・更新するまで最大 30 分かかる場合がある。

パブリックアクセス検証用関数(Principal: "*")の finding を確認する。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-lambda-public-test"]}}' \
  --query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[
    {
        "id": "<パブリック関数 finding ID>",
        "resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test",
        "resourceType": "AWS::Lambda::Function",
        "status": "ACTIVE"
    }
]

finding の詳細を確認する。

aws accessanalyzer get-finding-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --id <パブリック関数 finding ID> \
  --region ap-northeast-1 \
  --profile Audit
{
    "findingDetails": [
        {
            "externalAccessDetails": {
                "action": ["lambda:InvokeFunction"],
                "condition": {},
                "isPublic": true,
                "principal": {"AWS": "*"},
                "resourceControlPolicyRestriction": "NOT_APPLICABLE"
            }
        }
    ],
    "resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test",
    "status": "ACTIVE",
    "resourceType": "AWS::Lambda::Function",
    "findingType": "ExternalAccess",
    "resourceOwnerAccount": "<Workload アカウント ID>",
    "id": "<パブリック関数 finding ID>"
}

以下を確認する。

  • isPublictrue
  • principal{"AWS": "*"}
  • actionlambda:InvokeFunction
  • resourceControlPolicyRestrictionNOT_APPLICABLE

組織外アカウントからの実際のアクセス確認

組織外アカウントからパブリック関数への Invoke が成功することを確認する。

aws lambda invoke \
  --function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test \
  /tmp/lambda-output.json \
  --query '{StatusCode:StatusCode}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "StatusCode": 200
}

4. クロスアカウントアクセスの検出

クロスアカウントアクセス検証用関数の finding を確認する。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-lambda-crossaccount-test"]}}' \
  --query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[
    {
        "id": "<クロスアカウント関数 finding ID>",
        "resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test",
        "resourceType": "AWS::Lambda::Function",
        "status": "ACTIVE"
    }
]

finding の詳細を確認する。

aws accessanalyzer get-finding-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --id <クロスアカウント関数 finding ID> \
  --region ap-northeast-1 \
  --profile Audit
{
    "findingDetails": [
        {
            "externalAccessDetails": {
                "action": ["lambda:InvokeFunction"],
                "condition": {},
                "isPublic": false,
                "principal": {"AWS": "<組織外アカウント ID>"},
                "resourceControlPolicyRestriction": "NOT_APPLICABLE"
            }
        }
    ],
    "resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test",
    "status": "ACTIVE",
    "resourceType": "AWS::Lambda::Function",
    "findingType": "ExternalAccess",
    "resourceOwnerAccount": "<Workload アカウント ID>",
    "id": "<クロスアカウント関数 finding ID>"
}

以下を確認する。

  • isPublicfalse
  • principal に組織外アカウント ID が特定されている
  • actionlambda:InvokeFunction
  • resourceControlPolicyRestrictionNOT_APPLICABLE

組織外アカウントからの実際のアクセス確認

組織外アカウントからクロスアカウント関数への Invoke が成功することを確認する。

aws lambda invoke \
  --function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test \
  /tmp/lambda-output.json \
  --query '{StatusCode:StatusCode}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "StatusCode": 200
}

5. 予防コントロール(CT.LAMBDA.PV.2)との連動

本ステップは CT.LAMBDA.PV.2(SCP: add-permission で外部プリンシパルの設定をブロック)が有効な環境で実施する。CT.LAMBDA.PV.2 の詳細は CT.LAMBDA.PV.2 の検証記事 を参照。
CT.LAMBDA.PV.2 は SCP 型であり、S3・KMS・IAM ロールの検証で使用した RCP 型とは動作が異なる。RCP はリソースへのアクセス自体をブロックし、resourceControlPolicyRestrictionAPPLICABLE に変わる。一方、SCP はパブリック・クロスアカウント両方の add-permission API 呼び出しをブロックするが、既に設定されたリソースベースポリシーには影響しない。そのため、SCP を有効化しても、既に外部アクセスが設定された関数への Invoke は引き続き成功し、resourceControlPolicyRestrictionNOT_APPLICABLE のまま変わらない。

CT.LAMBDA.PV.2 の有効化確認

CT.LAMBDA.PV.2 が有効であることを確認する。

aws controltower list-enabled-controls \
  --target-identifier <対象 OU ARN> \
  --query "enabledControls[?controlIdentifier=='arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy'].{controlIdentifier:controlIdentifier,statusSummary:statusSummary}" \
  --profile Master
[]

無効の場合は有効化する。

aws controltower enable-control \
  --control-identifier arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy \
  --target-identifier <対象 OU ARN> \
  --profile Master
{
    "operationIdentifier": "<オペレーション ID>"
}

既存のパブリックアクセスがブロックされないことの確認

CT.LAMBDA.PV.2 が有効な状態でも、既存のリソースベースポリシー(Principal: "*")による Invoke はブロックされないことを確認する。

aws lambda invoke \
  --function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test \
  /tmp/lambda-output.json \
  --query '{StatusCode:StatusCode}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "StatusCode": 200
}

SCP は API 呼び出しをブロックするだけで、既存のリソースベースポリシーによるアクセスには遡及しない。

既存のクロスアカウント関数への Invoke も同様にブロックされないことを確認する。

aws lambda invoke \
  --function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test \
  /tmp/lambda-output.json \
  --query '{StatusCode:StatusCode}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "StatusCode": 200
}

新規の add-permission がブロックされることの確認

CT.LAMBDA.PV.2 が有効な状態で、新規の add-permissionPrincipal: "*")がブロックされることを確認する。

aws lambda add-permission \
  --function-name iaa-lambda-crossaccount-test \
  --statement-id public-access-blocked \
  --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:iaa-lambda-crossaccount-test with an explicit deny in a service control policy

resourceControlPolicyRestriction の確認

再スキャンを実行し、SCP 型では resourceControlPolicyRestriction が変わらないことを確認する。

aws accessanalyzer start-resource-scan \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --resource-arn arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test \
  --resource-owner-account <Workload アカウント ID> \
  --region ap-northeast-1 \
  --profile Audit
(出力なし)
aws accessanalyzer get-finding-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --id <パブリック関数 finding ID> \
  --region ap-northeast-1 \
  --profile Audit

resourceControlPolicyRestrictionNOT_APPLICABLE のまま変わらないことを確認する。SCP 型のコントロールは IAM Access Analyzer の resourceControlPolicyRestriction には反映されない。また、RCP 型では再スキャンにより旧 finding が RESOLVED になり新しい finding ID で再生成されたが、SCP 型では finding ID も変わらない。

6. クリーンアップ

CT.LAMBDA.PV.2 の無効化

ステップ 5 で CT.LAMBDA.PV.2 を有効化した場合のみ無効化する。元々有効な環境ではそのまま有効にしておくこと。
aws controltower disable-control \
  --control-identifier arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy \
  --target-identifier <対象 OU ARN> \
  --profile Master
{
    "operationIdentifier": "<オペレーション ID>"
}

リソースベースポリシーの削除

aws lambda remove-permission \
  --function-name iaa-lambda-public-test \
  --statement-id public-access
(出力なし)
aws lambda remove-permission \
  --function-name iaa-lambda-crossaccount-test \
  --statement-id crossaccount-access
(出力なし)

finding の確認

リソースベースポリシー削除後、finding が RESOLVED になることを確認する。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"resourceType": {"eq": ["AWS::Lambda::Function"]}}' \
  --query 'findings[].{id:id,resource:resource,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[]

テスト用関数の削除

aws lambda delete-function \
  --function-name iaa-lambda-public-test
(出力なし)
aws lambda delete-function \
  --function-name iaa-lambda-crossaccount-test
(出力なし)

Lambda 実行ロールの削除

aws iam delete-role \
  --role-name iaa-lambda-test-role
(出力なし)

Amazonアソシエイトリンク