serverless-security
Serverless security — AWS Lambda, GCP Cloud Functions, and Azure Functions
You are a serverless security specialist. Secure AWS Lambda, GCP Cloud Functions, and Azure Functions.
Serverless Attack Surface
| Vector | Risk | Example | |--------|------|---------| | Event injection | Malicious payload in event sources | S3 event with crafted key | | Function tampering | Unauthorized function modification | Weak IAM on function CRUD | | Dependency vulns | Third-party package CVEs | log4shell in Java function | | Secrets leakage | Environment variable exposure | Plaintext DB creds in env | | Denial of wallet | Cost explosion via invocation | Infinite loop or flood | | Cold boot | Residual data in execution env | Sensitive files from prior invocation | | Event bridge | Cross-account event injection | Unvalidated EventBridge rules |
AWS Lambda Security
# IAM policy — least privilege example
aws iam create-role --role-name lambda-minimal --assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# Attach minimal policy
aws iam put-role-policy --role-name lambda-minimal --policy-name minimal \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": "dynamodb:GetItem",
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/my-table"
}
]
}'
# Secure Lambda handler
import os
import json
from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.utilities.validation import validator
logger = Logger()
tracer = Tracer()
# Input validation schema
SCHEMA = {
"type": "object",
"properties": {
"user_id": {"type": "string", "pattern": "^[A-Za-z0-9-]+$"},
"action": {"type": "string", "enum": ["read", "write", "delete"]}
},
"required": ["user_id", "action"]
}
@tracer.capture_method
def get_secret():
"""Retrieve secret at runtime, not in env vars."""
from aws_lambda_powertools.utilities import parameters
return parameters.get_secret("/prod/db/password")
@validator(inbound_schema=SCHEMA)
def lambda_handler(event, context):
# Validate request context
if 'source' in event and event['source'] == 'aws.events':
return {'status': 'health_ok'}
user_id = event['user_id']
# Get secrets at runtime
db_password = get_secret()
# Authorize (check JWT or API key)
auth_header = event.get('headers', {}).get('Authorization', '')
logger.info(f"Processing user {user_id}")
return {
'statusCode': 200,
'body': json.dumps({'result': 'success'})
}
Function URL Security
# SAM template.yaml
Resources:
SecureFunction:
Type: AWS::Serverless::Function
Properties:
FunctionUrl:
AuthType: AWS_IAM
Cors:
AllowOrigins:
- https://app.example.com
Policies:
- Statement:
- Effect: Allow
Action: lambda:InvokeFunctionUrl
Principal: "*"
Condition:
StringEquals:
aws:SourceVpce: vpce-xxx
GCP Cloud Functions
# Deploy with security options
gcloud functions deploy secure-function \
--runtime python312 \
--entry-point handler \
--trigger-http \
--allow-unauthenticated=false \
--security-level secure-always \
--ingress-settings internal-only \
--max-instances 10 \
--timeout 60
# IAM (invoke only)
gcloud functions add-iam-policy-binding secure-function \
--member="serviceAccount:app-sa@project.iam.gserviceaccount.com" \
--role="roles/cloudfunctions.invoker"
Azure Functions
# Deploy with security
az functionapp create \
--name secure-func \
--runtime python \
--functions-version 4 \
--https-only true \
--ftps-state Disabled \
--min-tls-version 1.2
Secrets Management
# AWS Lambda — Parameter Store
ssm = boto3.client('ssm')
password = ssm.get_parameter(Name='/prod/db/password', WithDecryption=True)
# GCP — Secret Manager
from google.cloud import secretmanager
client = secretmanager.SecretManagerServiceClient()
secret = client.access_secret_version(name='projects/p/secrets/db/versions/latest')
# Azure — Key Vault
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
client = SecretClient(vault_url="https://kv.vault.azure.net", credential=credential)
secret = client.get_secret("db-password")
Denial of Wallet Prevention
# AWS — reserved concurrency (limits max parallel executions)
ReservedConcurrentExecutions: 10
# AWS — function URL throttling
# Via Lambda -> Function URL -> Throttle
# Set billing alerts
# AWS Budgets: $100/month with 80% threshold alert
# CloudWatch alarms on invocations
# Invocations > 10000 in 1 hour -> alarm + disable function trigger
Security Checklist
□ IAM: least privilege (no wildcard actions)
□ IAM: separate roles per function
□ Secrets: never in env vars, use Parameter Store / Secret Manager
□ Input validation: always validate event payloads
□ VPC: functions in private subnets for DB access
□ VPC: no public internet access (unless needed)
□ CORS: restrict origins on function URLs
□ Dependencies: scan with trivy/grype
□ Concurrency: set reserved concurrency limits
□ DLQ: configure dead letter queue for failures
□ Monitoring: CloudWatch/Loki logs + alerts
□ Logging: never log sensitive data