Lambda Function URL CORS Does Not Allow Wildcard Origins
Overview
This check examines AWS Lambda function URL CORS (Cross-Origin Resource Sharing) configurations to identify if wildcard origins (*) are permitted. CORS controls which websites can call your Lambda function URL from a browser. When you allow all origins with a wildcard, any website on the internet can make requests to your function.
Risk
Allowing wildcard origins in your Lambda function URL creates several security vulnerabilities:
- Data exposure: Any website can call your endpoint and read the response, potentially exposing sensitive data
- Unauthorized actions: Malicious sites can trigger state-changing operations on behalf of unsuspecting users
- Cross-origin attacks: Attackers can craft malicious web pages that exploit your function
- Credential theft: If
AllowCredentialsis also enabled, cookies and authentication headers can be stolen
Remediation Steps
Prerequisites
- AWS account access with permissions to modify Lambda function URLs
- Knowledge of which domains legitimately need to call your function
Required IAM permissions
You will need the following permissions:
lambda:GetFunctionUrlConfig- View current function URL configurationlambda:UpdateFunctionUrlConfig- Modify CORS settingslambda:ListFunctionUrlConfigs- List all function URLs (optional)
AWS Console Method
Step 1: Navigate to your Lambda function
- Sign in to the AWS Management Console
- Navigate to Lambda > Functions
- Select the Lambda function flagged by Prowler
Step 2: View the current Function URL configuration
- Click the Configuration tab
- In the left sidebar, click Function URL
- Review the current CORS settings under "Cross-origin resource sharing (CORS)"
Step 3: Update CORS to use specific origins
- Click Edit next to the Function URL configuration
- Scroll to the Cross-origin resource sharing (CORS) section
- Under Allow origin, remove the wildcard (
*) and add your specific trusted domains:- Example:
https://www.example.com - Example:
https://app.example.com
- Example:
- Optionally, restrict other CORS settings:
- Allow headers: List only headers your function needs (e.g.,
Content-Type,Authorization) - Allow methods: Select only the HTTP methods your function uses (e.g.,
GET,POST) - Allow credentials: Disable unless your function requires cookies or authentication headers
- Max age: Set a reasonable cache duration (e.g.,
3600seconds)
- Allow headers: List only headers your function needs (e.g.,
- Click Save
AWS CLI (optional)
View current CORS configuration:
aws lambda get-function-url-config \
--function-name <your-function-name> \
--region us-east-1
Update CORS to allow specific origins:
aws lambda update-function-url-config \
--function-name <your-function-name> \
--cors '{
"AllowOrigins": ["https://www.example.com", "https://app.example.com"],
"AllowMethods": ["GET", "POST"],
"AllowHeaders": ["Content-Type", "Authorization"],
"AllowCredentials": false,
"MaxAge": 3600
}' \
--region us-east-1
Using shorthand syntax:
aws lambda update-function-url-config \
--function-name <your-function-name> \
--cors 'AllowOrigins=https://www.example.com,https://app.example.com,AllowMethods=GET,POST,AllowHeaders=Content-Type,Authorization,AllowCredentials=false,MaxAge=3600' \
--region us-east-1
List all function URLs to find affected functions:
aws lambda list-functions \
--region us-east-1 \
--query 'Functions[*].FunctionName' \
--output text | tr '\t' '\n' | while read fn; do
aws lambda get-function-url-config --function-name "$fn" --region us-east-1 2>/dev/null
done
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda function with secure CORS configuration
Parameters:
AllowedOrigin:
Type: String
Default: 'https://www.example.com'
Description: The origin allowed to access this function URL
Resources:
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: SecureLambdaExecutionRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: my-secure-function
Runtime: python3.12
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: |
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': 'Hello from Lambda!'
}
LambdaFunctionUrl:
Type: AWS::Lambda::Url
Properties:
AuthType: AWS_IAM
TargetFunctionArn: !GetAtt MyLambdaFunction.Arn
Cors:
AllowOrigins:
- !Ref AllowedOrigin
AllowMethods:
- GET
- POST
AllowHeaders:
- Content-Type
- Authorization
AllowCredentials: false
MaxAge: 3600
LambdaFunctionUrlPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref MyLambdaFunction
Action: lambda:InvokeFunctionUrl
Principal: '*'
FunctionUrlAuthType: AWS_IAM
Outputs:
FunctionUrl:
Description: The Lambda function URL
Value: !GetAtt LambdaFunctionUrl.FunctionUrl
FunctionArn:
Description: The Lambda function ARN
Value: !GetAtt MyLambdaFunction.Arn
Deploy the template:
aws cloudformation deploy \
--template-file lambda-secure-cors.yaml \
--stack-name lambda-secure-cors-stack \
--parameter-overrides AllowedOrigin=https://www.example.com \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)
provider "aws" {
region = "us-east-1"
}
variable "allowed_origins" {
type = list(string)
default = ["https://www.example.com", "https://app.example.com"]
description = "List of origins allowed to access the Lambda function URL"
}
# IAM role for Lambda
resource "aws_iam_role" "lambda_role" {
name = "secure-lambda-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}]
})
}
# Attach basic execution policy
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = aws_iam_role.lambda_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
# Lambda function
resource "aws_lambda_function" "my_function" {
filename = "function.zip"
function_name = "my-secure-function"
role = aws_iam_role.lambda_role.arn
handler = "index.lambda_handler"
runtime = "python3.12"
source_code_hash = filebase64sha256("function.zip")
}
# Lambda function URL with secure CORS
resource "aws_lambda_function_url" "my_function_url" {
function_name = aws_lambda_function.my_function.function_name
authorization_type = "AWS_IAM"
cors {
allow_origins = var.allowed_origins
allow_methods = ["GET", "POST"]
allow_headers = ["Content-Type", "Authorization"]
allow_credentials = false
max_age = 3600
}
}
output "function_url" {
description = "The Lambda function URL"
value = aws_lambda_function_url.my_function_url.function_url
}
Verification
After completing the remediation:
- Navigate to Lambda > Functions > your function in the AWS Console
- Click Configuration > Function URL
- Verify that "Allow origin" shows your specific domains, not
* - Test your application to ensure legitimate cross-origin requests still work
- Re-run the Prowler check to confirm the issue is resolved
CLI verification commands
Verify the CORS configuration:
aws lambda get-function-url-config \
--function-name <your-function-name> \
--region us-east-1 \
--query 'Cors'
Test that wildcard is no longer present:
aws lambda get-function-url-config \
--function-name <your-function-name> \
--region us-east-1 \
--query 'Cors.AllowOrigins' \
--output text | grep -q '\*' && echo "FAIL: Wildcard still present" || echo "PASS: No wildcard origin"
Additional Resources
- Configuring CORS for a Lambda function URL
- AWS Lambda Function URLs
- AWS Lambda CORS API Reference
- CORS Security Best Practices (OWASP)
Notes
- Consider IAM authentication: If your function handles sensitive data, use
AWS_IAMauthentication type instead ofNONEfor an additional layer of security - AllowCredentials warning: If you enable
AllowCredentials: true, you cannot use wildcard origins anyway (browsers enforce this restriction), but you should still specify only trusted origins - Development environments: For local development, you may add
http://localhost:3000to allowed origins, but ensure this is removed in production - Multiple origins: Lambda function URLs support up to 100 allowed origins, so you can specify all legitimate domains that need access
- API Gateway alternative: For more granular CORS control and additional security features, consider using API Gateway with Lambda integration instead of Lambda function URLs