AWS Lambda Security Audit
Audit AWS Lambda functions across runtimes (Node, Python, Go, Java, .NET, Ruby).
When this skill applies
- Reviewing Lambda IAM roles and policies
- Auditing function configuration (env vars, VPC, timeout, memory)
- Reviewing Function URL vs API Gateway exposure
- Checking layer dependencies and supply chain
- Auditing handler code for runtime-agnostic Lambda concerns
Workflow
Follow ../_shared/audit-workflow.md. Companion: runtime-specific skills (nodejs-express-security, fastapi-security, etc.).
Phase 1: Stack detection
# IaC discovery
ls serverless.yml serverless.yaml template.yaml template.yml cdk.json 2>/dev/null
# Check AWS CLI
aws --version 2>/dev/null
# SAM
ls samconfig.toml 2>/dev/null
Phase 2: Inventory
# Function definitions
grep -rn 'AWS::Lambda::Function\|Type: AWS::Serverless::Function\|new Function(' . --include='*.yml' --include='*.yaml' --include='*.ts' --include='*.py' 2>/dev/null
# IAM policies
grep -rn 'Policies:\|PolicyDocument\|inlinePolicies' . --include='*.yml' --include='*.yaml' --include='*.ts' 2>/dev/null | head
# Function URLs
grep -nE 'FunctionUrlConfig|addFunctionUrl' . --include='*.yml' --include='*.yaml' --include='*.ts' 2>/dev/null
# Env vars
grep -nE 'Environment:|environment:' . --include='*.yml' --include='*.yaml' 2>/dev/null | head
Phase 3: Detection — the checks
IAM — least privilege
Function URL exposure
Function URLs are public HTTPS endpoints without API Gateway. They're easy and risky.
- AWL-URL-1 Function URLs use
AuthType: AWS_IAM if not public; NONE only for genuinely public endpoints.
- AWL-URL-2 With
AuthType: NONE, the Lambda code is the only line of defense — every request validated.
- AWL-URL-3 CORS configured at the Function URL level for browser-facing URLs (specific origins).
- AWL-URL-4 Function URL invokes only the function's
$LATEST or a specific alias; not used to expose internal functions.
API Gateway integration
If behind API Gateway:
- AWL-AG-1 API Gateway authorizers configured (Lambda Authorizer, Cognito, JWT) for non-public endpoints.
- AWL-AG-2 API keys + usage plans for B2B APIs.
- AWL-AG-3 Throttling configured per route.
- AWL-AG-4 Request validation at API Gateway level (catches malformed requests before Lambda invocation, reducing cost surface).
- AWL-AG-5 Resource policies restrict access to specific VPCs / IPs if private.
Environment variables
- AWL-ENV-1 Sensitive env vars encrypted with KMS (
KmsKeyArn). Default AWS-managed key works; CMK for sensitive cases.
- AWL-ENV-2 Encryption helpers used to decrypt at runtime, with cache so KMS isn't called every invocation.
- AWL-ENV-3 Secrets fetched from Secrets Manager / Parameter Store at cold start, NOT in env vars (rotation works).
- AWL-ENV-4 No secrets in CloudFormation parameters without
NoEcho: true.
VPC configuration
- AWL-VPC-1 Functions in VPC only when needed (database access, internal APIs). VPC adds cold start latency.
- AWL-VPC-2 Security groups restrict outbound to specific destinations.
- AWL-VPC-3 Subnets are private; NAT Gateway / VPC Endpoints for outbound.
- AWL-VPC-4 Lambda doesn't need internet → use VPC endpoints (PrivateLink) instead of NAT.
Reserved / provisioned concurrency
- AWL-CC-1 Reserved concurrency caps functions that have downstream rate limits or backend bottlenecks.
- AWL-CC-2 Provisioned concurrency for latency-sensitive functions; not enabled needlessly (cost).
Cold start state leakage
Lambda containers persist across invocations from the same warm runtime. Module-level state leaks across users.
// BAD — caches per-user data in module scope
let currentUser;
exports.handler = async (event) => {
currentUser = event.user; // overwritten by next invocation
return process(currentUser);
};
// GOOD — per-invocation scope only
exports.handler = async (event) => {
const user = event.user;
return process(user);
};
- AWL-CS-1 No mutable module-scope state holding per-request data.
- AWL-CS-2 Connection pools (DB, HTTP) initialized at cold start are OK; per-request state in invocation scope.
Timeouts and memory
- AWL-TO-1 Function timeout appropriate (default 3s; max 15min). Excessive timeout = DoS amplifier.
- AWL-TO-2 Memory size tested; underprovisioning causes slow execution + cost; overprovisioning is waste but not security.
Layers and dependencies
- AWL-LY-1 Lambda Layers from your own account or trusted publishers (AWS, well-known). Public layer ARNs verified.
- AWL-LY-2 Layer versions pinned; auto-update not in use without testing.
- AWL-LY-3 Bundled dependencies (zip) scanned (
pip-audit, npm audit, etc.) before deploy.
Container image deployment
If Lambda uses container images:
- AWL-CT-1 Base image from trusted source; minimal (distroless, alpine).
- AWL-CT-2 Image scanned (ECR scanning, Snyk, Trivy) before deploy.
- AWL-CT-3 Image doesn't contain build secrets (multi-stage build hides them).
Async invocation and DLQ
- AWL-AS-1 Dead-letter queue configured for async invocations; failures don't disappear.
- AWL-AS-2 Retry config (
Maximum Retry Attempts) appropriate; not retrying poison messages indefinitely.
- AWL-AS-3 Idempotency for async handlers (S3 event, SQS, etc.) — same event may invoke multiple times.
Logs and X-Ray
- AWL-LOG-1 CloudWatch Logs retention set (default infinite; choose 30-90 days for production, longer for compliance).
- AWL-LOG-2 Sensitive data not logged. CloudWatch Logs are searchable by IAM-authorized users.
- AWL-LOG-3 X-Ray sampling configured; traces don't include raw request bodies.
Event source mappings
- AWL-ESM-1 SQS / Kinesis / DynamoDB streams as event sources — Lambda role has only
Read/Delete permissions on the source.
- AWL-ESM-2 Cross-account event source: source ARN restricted.
Signing / code integrity
- AWL-SIG-1 Code Signing Configuration enabled for production functions if compliance requires.
- AWL-SIG-2 Signing profile and signed deployment package via AWS Signer.
Throttling / DoS protection
- AWL-DOS-1 Account concurrency limit known and monitored; one runaway function shouldn't exhaust account limit.
- AWL-DOS-2 Function-level reserved concurrency (per AWL-CC-1).
- AWL-DOS-3 Async invocation queues bounded (DLQ catches overflow).
Deployment
- AWL-DEP-1 CI/CD deploys with assume-role + short-lived creds; not long-lived IAM user keys.
- AWL-DEP-2 Production deploy gated; staging tested first.
Phase 4: Triage
Critical: IAM role with *:*; secrets in plain env vars; Function URL with AuthType NONE and no in-code auth; module-scope mutable state across invocations.
Phase 5: Report
Use ../_shared/findings-schema.md. Prefix IDs with AWL-.
Source: hlsitechio/claude-skills-security — distributed by TomeVault.
1---2name: aws-lambda-security3description: Security audit for AWS Lambda functions including IAM role least privilege, environment variable encryption (KMS), Function URLs vs API Gateway, VPC config, layer usage, container image scanning, X-Ray and logs PII, cold start state, async invocation handling, and Lambda-specific patterns across Node, Python, Go, Java runtimes. Use this skill whenever the user mentions AWS Lambda, lambda function, IAM role, Function URL, API Gateway + Lambda, Lambda layer, SAM, CDK Lambda, Serverless Framework, or asks "audit my Lambda", "Lambda security review", "Lambda IAM". Trigger when the codebase contains `serverless.yml`, `template.yaml` (SAM), `cdk.json`, or Lambda handler patterns. Use when this capability is needed.4---56# AWS Lambda Security Audit78Audit AWS Lambda functions across runtimes (Node, Python, Go, Java, .NET, Ruby).910## When this skill applies1112- Reviewing Lambda IAM roles and policies13- Auditing function configuration (env vars, VPC, timeout, memory)14- Reviewing Function URL vs API Gateway exposure15- Checking layer dependencies and supply chain16- Auditing handler code for runtime-agnostic Lambda concerns1718## Workflow1920Follow `../_shared/audit-workflow.md`. Companion: runtime-specific skills (`nodejs-express-security`, `fastapi-security`, etc.).2122### Phase 1: Stack detection2324```bash25# IaC discovery26ls serverless.yml serverless.yaml template.yaml template.yml cdk.json 2>/dev/null27# Check AWS CLI28aws --version 2>/dev/null29# SAM30ls samconfig.toml 2>/dev/null31```3233### Phase 2: Inventory3435```bash36# Function definitions37grep -rn 'AWS::Lambda::Function\|Type: AWS::Serverless::Function\|new Function(' . --include='*.yml' --include='*.yaml' --include='*.ts' --include='*.py' 2>/dev/null3839# IAM policies40grep -rn 'Policies:\|PolicyDocument\|inlinePolicies' . --include='*.yml' --include='*.yaml' --include='*.ts' 2>/dev/null | head4142# Function URLs43grep -nE 'FunctionUrlConfig|addFunctionUrl' . --include='*.yml' --include='*.yaml' --include='*.ts' 2>/dev/null4445# Env vars46grep -nE 'Environment:|environment:' . --include='*.yml' --include='*.yaml' 2>/dev/null | head47```4849### Phase 3: Detection — the checks5051#### IAM — least privilege5253- **AWL-IAM-1** Each function has its own role. Don't share one fat role across functions.54- **AWL-IAM-2** Policies grant specific actions on specific resources (no `*`).55 ```yaml56 # BAD57 Policies:58 - Action: '*'59 Resource: '*'60 61 # GOOD62 Policies:63 - Action:64 - dynamodb:GetItem65 - dynamodb:PutItem66 Resource:67 - !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/Users'68 ```69- **AWL-IAM-3** No `iam:PassRole`, `iam:CreateRole`, `sts:AssumeRole` unless needed (and then scoped).70- **AWL-IAM-4** No `kms:Decrypt: *` — limit to specific keys.71- **AWL-IAM-5** Service-linked roles (e.g., `AWSLambdaVPCAccessExecutionRole`) granted only when VPC actually needed.72- **AWL-IAM-6** Resource-based policies on Lambda (`AWS::Lambda::Permission`) restrict who can invoke (specific principal, source ARN).7374#### Function URL exposure7576Function URLs are public HTTPS endpoints without API Gateway. They're easy and risky.7778- **AWL-URL-1** Function URLs use `AuthType: AWS_IAM` if not public; `NONE` only for genuinely public endpoints.79- **AWL-URL-2** With `AuthType: NONE`, the Lambda code is the only line of defense — every request validated.80- **AWL-URL-3** CORS configured at the Function URL level for browser-facing URLs (specific origins).81- **AWL-URL-4** Function URL invokes only the function's `$LATEST` or a specific alias; not used to expose internal functions.8283#### API Gateway integration8485If behind API Gateway:8687- **AWL-AG-1** API Gateway authorizers configured (Lambda Authorizer, Cognito, JWT) for non-public endpoints.88- **AWL-AG-2** API keys + usage plans for B2B APIs.89- **AWL-AG-3** Throttling configured per route.90- **AWL-AG-4** Request validation at API Gateway level (catches malformed requests before Lambda invocation, reducing cost surface).91- **AWL-AG-5** Resource policies restrict access to specific VPCs / IPs if private.9293#### Environment variables9495- **AWL-ENV-1** Sensitive env vars encrypted with KMS (`KmsKeyArn`). Default AWS-managed key works; CMK for sensitive cases.96- **AWL-ENV-2** Encryption helpers used to decrypt at runtime, with cache so KMS isn't called every invocation.97- **AWL-ENV-3** Secrets fetched from Secrets Manager / Parameter Store at cold start, NOT in env vars (rotation works).98- **AWL-ENV-4** No secrets in CloudFormation parameters without `NoEcho: true`.99100#### VPC configuration101102- **AWL-VPC-1** Functions in VPC only when needed (database access, internal APIs). VPC adds cold start latency.103- **AWL-VPC-2** Security groups restrict outbound to specific destinations.104- **AWL-VPC-3** Subnets are private; NAT Gateway / VPC Endpoints for outbound.105- **AWL-VPC-4** Lambda doesn't need internet → use VPC endpoints (PrivateLink) instead of NAT.106107#### Reserved / provisioned concurrency108109- **AWL-CC-1** Reserved concurrency caps functions that have downstream rate limits or backend bottlenecks.110- **AWL-CC-2** Provisioned concurrency for latency-sensitive functions; not enabled needlessly (cost).111112#### Cold start state leakage113114Lambda containers persist across invocations from the same warm runtime. Module-level state leaks across users.115116```js117// BAD — caches per-user data in module scope118let currentUser;119exports.handler = async (event) => {120 currentUser = event.user; // overwritten by next invocation121 return process(currentUser);122};123124// GOOD — per-invocation scope only125exports.handler = async (event) => {126 const user = event.user;127 return process(user);128};129```130131- **AWL-CS-1** No mutable module-scope state holding per-request data.132- **AWL-CS-2** Connection pools (DB, HTTP) initialized at cold start are OK; per-request state in invocation scope.133134#### Timeouts and memory135136- **AWL-TO-1** Function timeout appropriate (default 3s; max 15min). Excessive timeout = DoS amplifier.137- **AWL-TO-2** Memory size tested; underprovisioning causes slow execution + cost; overprovisioning is waste but not security.138139#### Layers and dependencies140141- **AWL-LY-1** Lambda Layers from your own account or trusted publishers (AWS, well-known). Public layer ARNs verified.142- **AWL-LY-2** Layer versions pinned; auto-update not in use without testing.143- **AWL-LY-3** Bundled dependencies (zip) scanned (`pip-audit`, `npm audit`, etc.) before deploy.144145#### Container image deployment146147If Lambda uses container images:148149- **AWL-CT-1** Base image from trusted source; minimal (distroless, alpine).150- **AWL-CT-2** Image scanned (ECR scanning, Snyk, Trivy) before deploy.151- **AWL-CT-3** Image doesn't contain build secrets (multi-stage build hides them).152153#### Async invocation and DLQ154155- **AWL-AS-1** Dead-letter queue configured for async invocations; failures don't disappear.156- **AWL-AS-2** Retry config (`Maximum Retry Attempts`) appropriate; not retrying poison messages indefinitely.157- **AWL-AS-3** Idempotency for async handlers (S3 event, SQS, etc.) — same event may invoke multiple times.158159#### Logs and X-Ray160161- **AWL-LOG-1** CloudWatch Logs retention set (default infinite; choose 30-90 days for production, longer for compliance).162- **AWL-LOG-2** Sensitive data not logged. CloudWatch Logs are searchable by IAM-authorized users.163- **AWL-LOG-3** X-Ray sampling configured; traces don't include raw request bodies.164165#### Event source mappings166167- **AWL-ESM-1** SQS / Kinesis / DynamoDB streams as event sources — Lambda role has only `Read/Delete` permissions on the source.168- **AWL-ESM-2** Cross-account event source: source ARN restricted.169170#### Signing / code integrity171172- **AWL-SIG-1** Code Signing Configuration enabled for production functions if compliance requires.173- **AWL-SIG-2** Signing profile and signed deployment package via AWS Signer.174175#### Throttling / DoS protection176177- **AWL-DOS-1** Account concurrency limit known and monitored; one runaway function shouldn't exhaust account limit.178- **AWL-DOS-2** Function-level reserved concurrency (per AWL-CC-1).179- **AWL-DOS-3** Async invocation queues bounded (DLQ catches overflow).180181#### Deployment182183- **AWL-DEP-1** CI/CD deploys with assume-role + short-lived creds; not long-lived IAM user keys.184- **AWL-DEP-2** Production deploy gated; staging tested first.185186### Phase 4: Triage187188Critical: IAM role with `*:*`; secrets in plain env vars; Function URL with AuthType NONE and no in-code auth; module-scope mutable state across invocations.189190### Phase 5: Report191192Use `../_shared/findings-schema.md`. Prefix IDs with `AWL-`.193194---195> Source: [hlsitechio/claude-skills-security](https://github.com/hlsitechio/claude-skills-security) — distributed by [TomeVault](https://tomevault.io).196<!-- tomevault:4.0:skill_md:2026-06-15 -->