Description

Ensure that the resource-based policy of every AWS Lambda function does not allow access to everyone. A policy is public when an Allow statement uses a wildcard principal (*, {"AWS": "*"} or arn:aws:iam::*:root) and has no condition that restricts the caller, for example by source account, source ARN, organization or a private or trusted IP address range. Functions without a resource-based policy are treated as having an empty policy and pass.

Rationale

A public resource-based policy lets anyone, including users in other AWS accounts, invoke the function and run its code with the permissions of its execution role. This can lead to data exfiltration, unauthorized changes and invocation floods that cause throttling and unexpected costs. Function policies should grant access only to specific accounts, roles or AWS services, limited with conditions such as AWS:SourceAccount and AWS:SourceArn.

Impact

  • Prevents unknown principals from invoking the function or changing its configuration

  • Reduces the risk of data exposure, unauthorized changes and unexpected costs

  • Callers that rely on the public permission, such as a function URL with auth type NONE or a trigger from another account, will be denied until a permission for that specific principal is added

Default Value

New Lambda functions have no resource-based policy. Statements are added only when a permission is granted, for example when a trigger or function URL is configured, and a public statement exists only if a wildcard principal was used.

Pre-Requisites

  • IAM permissions: lambda:ListFunctions, lambda:GetPolicy, lambda:RemovePermission and lambda:AddPermission

  • A list of the accounts, roles and AWS services that legitimately need to invoke each function

  • Confirmation from the application owner that no required caller depends on the public permission

Remediation

Test Plan

Console:

  1. Sign in to the AWS Management Console at https://console.aws.amazon.com.

  2. Open the AWS Lambda console and select Functions.

  3. Select a function, open the Configuration tab and select Permissions.

  4. In Resource-based policy statements, review each statement and check whether its principal is *, {"AWS": "*"} or arn:aws:iam::*:root without a condition such as AWS:SourceAccount, AWS:SourceArn, aws:PrincipalOrgID or aws:SourceIp.

  5. Repeat for each function in each region in use.

  6. If such a statement exists, follow the implementation steps.

CLI:

List the functions:

aws lambda list-functions --region <REGION> --query "Functions[].FunctionName" --output table

Show the resource-based policy of a function:

aws lambda get-policy --region <REGION> --function-name <FUNCTION_NAME> --query "Policy" --output text

A function fails if any Allow statement has the principal *, {"AWS": "*"} or arn:aws:iam::*:root and no condition that restricts access to a specific account, source ARN, organization or private or trusted IP range. A ResourceNotFoundException error means that the function has no policy; such functions pass.

Implementation Steps

Console:

  1. Sign in to the AWS Management Console at https://console.aws.amazon.com.

  2. Open the AWS Lambda console and select the function.

  3. Open the Configuration tab and select Permissions.

  4. In Resource-based policy statements, select the public statement and choose Delete, then confirm.

  5. If access is still needed, choose Add permissions and grant access to a specific AWS account, role or AWS service only. For an AWS service, also set Source ARN or Source account.

  6. Repeat for each affected function in each region in use.

CLI:

Remove the public statement, using its Sid from the Test Plan output:

aws lambda remove-permission --region <REGION> --function-name <FUNCTION_NAME> --statement-id <STATEMENT_ID>

If access is still needed, allow a specific account to invoke the function:

aws lambda add-permission --region <REGION> --function-name <FUNCTION_NAME> --statement-id <NEW_STATEMENT_ID> --action lambda:InvokeFunction --principal <TRUSTED_ACCOUNT_ID>

If an AWS service needs access, limit it to the calling resource:

aws lambda add-permission --region <REGION> --function-name <FUNCTION_NAME> --statement-id <NEW_STATEMENT_ID> --action lambda:InvokeFunction --principal s3.amazonaws.com --source-arn <SOURCE_RESOURCE_ARN> --source-account <ACCOUNT_ID>

Backout Plan

Before making changes, save the current policy:

aws lambda get-policy --region <REGION> --function-name <FUNCTION_NAME> --query "Policy" --output text > function-policy-backup.json

To roll back:

  1. Sign in to the AWS Management Console at https://console.aws.amazon.com.

  2. Open the AWS Lambda console, select the function and open the Configuration tab.

  3. Select Permissions and, in Resource-based policy statements, choose Add permissions.

  4. Re-create the removed statement using the principal, action and conditions in the saved backup.

  5. Choose Save.

Note: rolling back makes the function publicly accessible again. Use it only as a temporary measure.

Reference