AWS Lambda Security: Execution Roles, Resource Policies, and Function URLs
AWS Lambda security that matters in prod: execution-role blast radius, resource-based policies, Function URL auth, public layers, and env vars that are still plaintext secrets.
Lambda is an IAM principal that runs your code when something invokes it. AWS Lambda security is who can invoke, what the execution role can do, and whether a Function URL made the function a public app. It is not SAM/CDK syntax trivia, not a cold-start guide, and not Amazon EventBridge security (that is the bus that may invoke you). AWS reference: Lambda security.
Invoker (IAM / URL / API GW / event source)
→ resource-based policy on the function
→ execution role —CAN_ACCESS→ S3, Secrets, RDS, STS
→ optional VPC ENI
If the execution role can s3:GetObject on the backup bucket, every XSS-in-a-dependency is a data path. Rank that with pod-to-cloud-admin thinking, even without Kubernetes.
1. Execution role is the blast radius
One role per function (or per blast-radius group). No AWSLambdaFullAccess. No s3:* “because the handler might grow.”
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::payments-inbox/*"
}
aws lambda get-function --function-name payments-ingest \
--query 'Configuration.[Role,Environment.Variables]'
Failure modes:
- Shared role across twenty functions; one function’s SSRF becomes twenty products.
- Role has
iam:PassRoleandlambda:CreateFunction—the function can spawn a sibling with a better role. - Env still holds
DATABASE_URLwith a password. Encrypt-at-rest does not hide it fromGetFunction.
Prefer IAM DB auth or Secrets Manager with a single secret ARN.
2. Resource-based policy is who may invoke
The execution role is outbound. The resource policy is inbound (lambda:InvokeFunction).
aws lambda get-policy --function-name payments-ingest
Typical statements: events.amazonaws.com with AWS:SourceArn equal to this rule; apigateway.amazonaws.com with the API ARN; a specific IAM role for partners.
Principal: "*" with no aws:SourceAccount / SourceArn is a public invoke API. S3 event notifications that omit the source ARN let another account’s bucket trigger you (confused deputy). Always condition on the event source ARN.
3. Function URLs
aws lambda get-function-url-config --function-name payments-ingest
AuthType |
Meaning |
|---|---|
NONE |
Anyone who knows the URL (or scans lambda-url.*.on.aws) can invoke |
AWS_IAM |
SigV4; still internet-reachable; lock with resource policy + IAM |
NONE is a public app. Put CloudFront + WAF in front and lock the URL the same way you lock an ALB origin (Amazon CloudFront security), or do not use Function URLs for prod.
CORS AllowOrigins: * on a NONE URL is a browser-callable endpoint for every site.
4. Layers, images, and /tmp
- Layers: you execute someone else’s zip. Pin layer version ARNs; do not use
:latest. Treat a public layer as supply chain (image provenance is the same idea for containers). - Container images: pull by digest from a repo your account owns; scan in CI (Trivy CI vs operator).
/tmp: 512–10 GB, shared across invokes on the same freeze. Do not write secrets there and assume the next customer is isolated; they are not across warm invokes of the same function, but you still should not persist credentials on disk.
5. Public invoke vs VPC
VPC attachment is for reaching private RDS, not for hiding invoke. Combine:
- Resource policy + no Function URL
NONE - RDS in private subnets, SG from the function SG (Amazon RDS security)
- S3/STS interface or gateway endpoints so the function does not need
0.0.0.0/0NAT (VPC endpoints vs NAT)
Checklist
- Execution role: named resources, no
*on data services, no PassRole to arbitrary roles - No secrets in env; CMK on env if you still have non-secrets config
- Resource policy: every event source ARN-conditioned; no
Principal:*without a hard condition - Function URL: not
NONEin prod, or fronted and origin-locked - Layers pinned; images by digest
- CloudWatch logs group retained and not public via resource policy
- Reserved concurrency on the functions that can stampede downstream IAM/RDS
A NONE Function URL plus s3:* is an internet-to-data path. Put it at the top of how to prioritize cloud vulnerabilities.
Related: Amazon RDS security · Amazon CloudFront security · Amazon EventBridge security