⚡ Serverless Security
Serverless Security Reference
AWS Lambda, Azure Functions, GCP Cloud Functions — attack surface, event injection, IAM, secrets, SSRF, and supply chain threats with detection and mitigation for each.
⚡ Serverless Threat Reference
Event Injection via API Gateway
Critical
T1190
API Gateway invokes Lambda with the raw event body — if Lambda passes user input to shell commands, SQL queries, or XML parsers without sanitisation, classic injection applies in a serverless context.
Detection
CloudWatch Logs: Lambda error patterns matching injection payloads. WAF: SQLi/XSS pattern matches on API Gateway.
Mitigation
AWS WAF in front of API Gateway. Input validation in Lambda code. Use parameterised queries — never string-format SQL.
Lambda code: NEVER do subprocess.run(f"command {event['body']}") or cursor.execute(f"SELECT * FROM users WHERE id={user_id}")
Event Injection via S3 Trigger
Critical
T1190
Lambda triggered by S3 PutObject events uses the filename or content in code logic. Attacker uploads a file with a malicious name (e.g., "; rm -rf /tmp/*") or content that triggers code execution.
Detection
CloudWatch Logs: Lambda function errors. S3 access logs: unusual object names. GuardDuty S3 protection.
Mitigation
Sanitise S3 object key before using in any command. Never execute filenames as commands. Process file content safely.
Python: key = event["Records"][0]["s3"]["object"]["key"] — this is attacker-controlled input. urllib.parse.unquote_plus it and validate before use.
Excessive IAM Permissions on Execution Role
Critical
T1078.004
Lambda execution role has AdministratorAccess or * permissions — if Lambda is exploited (via injection or supply chain), attacker inherits full AWS account access via IMDS/credential endpoint.
Detection
CloudTrail: API calls from Lambda execution role from unexpected operations (IAM writes, EC2 launches, etc.)
Mitigation
Apply least privilege to Lambda execution role. Use resource-based conditions. Lambda should only have exactly the permissions it needs.
aws lambda get-function-configuration --function-name NAME | grep Role — then check that role's policies.
SSRF from Lambda → IMDS Credential Access
Critical
T1552.005
If Lambda makes HTTP requests to user-supplied URLs (e.g., a URL fetcher, image processor, PDF generator), attacker submits http://169.254.169.254/latest/meta-data/iam/security-credentials/ — gets Lambda execution role credentials.
Detection
CloudWatch Logs: requests to 169.254.x.x from Lambda function. WAF: block SSRF patterns on API Gateway input.
Mitigation
Validate and allowlist URLs in Lambda code. Block private IP ranges in outbound requests. IMDSv2 doesn't help here — Lambda uses its own credential endpoint.
In Lambda: always validate URL schemes and destinations before making outbound HTTP requests.
Environment Variable Secret Exposure
High
T1552
Secrets (database passwords, API keys) stored in Lambda environment variables are exposed in: AWS console (visible to IAM users with read access), CloudFormation templates, exported function configs, Lambda deployment packages.
Detection
CloudTrail: GetFunctionConfiguration calls (reveals env vars) from unusual identities.
Mitigation
Use Secrets Manager or SSM Parameter Store — reference by name in code, not stored in env vars. Never log os.environ["API_KEY"].
aws lambda get-function-configuration --function-name NAME — anyone with this permission can see all env vars.
Dependency Confusion / Supply Chain Attack
High
T1195.001
Lambda deployment packages include third-party dependencies. If the package manager pulls a malicious package (typosquatting, namespace confusion, compromised registry), malicious code runs inside Lambda with its execution role permissions.
Detection
Unusual outbound connections from Lambda in CloudWatch Logs or VPC Flow Logs. Lambda execution behavior changes without code deployment.
Mitigation
Pin all dependencies to specific versions with hash verification. Use private package mirrors. Scan with Dependabot or Snyk in CI/CD. SBOMs for all Lambda packages.
requirements.txt or package.json pinned versions are not enough — also verify hashes.
Lambda Layer Tampering
High
T1195
Lambda Layers shared across functions — if an attacker modifies a shared layer (via IAM permissions on the layer), every function using that layer is compromised on next invocation.
Detection
CloudTrail: PublishLayerVersion from unexpected IAM identity.
Mitigation
Restrict PublishLayerVersion and UpdateFunctionConfiguration to CI/CD pipeline IAM roles only. Sign and verify layer versions.
aws lambda add-layer-version-permission — check who has permission to publish to shared layers.
Cold Start Timing — Sandbox Reuse Information Leak
High
T1497
Lambda execution environments are reused (warm starts). Residual data from previous invocations (global variables, /tmp filesystem contents) can leak to subsequent invocations of the same function if not properly cleaned.
Detection
Application-layer: look for data leakage in function responses that contains data from other users.
Mitigation
Clear /tmp between invocations. Never store sensitive data in global variables. Treat each invocation as potentially being served by a shared environment.
Python global state is NOT reset between warm invocations — user_data = {} at module level persists.
🔐 Serverless IAM — Least Privilege Reference
Lambda execution role — what to grant
| What Lambda Needs | Grant This | Not This |
|---|---|---|
| Write logs to CloudWatch | logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents | logs:* |
| Read from one S3 bucket | s3:GetObject on arn:aws:s3:::bucket-name/* | s3:* on * |
| Write to one S3 bucket | s3:PutObject on specific bucket ARN + path | s3:* |
| Read one secret | secretsmanager:GetSecretValue on specific secret ARN | secretsmanager:* |
| Write to DynamoDB table | dynamodb:PutItem, dynamodb:UpdateItem on table ARN | dynamodb:* |
| Invoke another Lambda | lambda:InvokeFunction on specific function ARN | lambda:* |
| Send SQS message | sqs:SendMessage on specific queue ARN | sqs:* |
| VPC access (ENI creation) | ec2:CreateNetworkInterface, ec2:DescribeNetworkInterfaces, ec2:DeleteNetworkInterface | ec2:* |
Audit Lambda execution roles
#!/bin/bash
# Audit all Lambda execution roles for over-privilege
for func in $(aws lambda list-functions --query "Functions[*].FunctionName" --output text); do
role=$(aws lambda get-function-configuration --function-name "$func" \
--query "Role" --output text)
echo -e "\n=== $func ==="
echo "Role: $role"
# Get attached policies
role_name=$(echo $role | sed 's/.*role\///')
aws iam list-attached-role-policies --role-name "$role_name" \
--query "AttachedPolicies[*].PolicyName" --output text
# Flag if admin policy
aws iam list-attached-role-policies --role-name "$role_name" \
--query "AttachedPolicies[?PolicyName=='AdministratorAccess']" --output text | \
grep -q "AdministratorAccess" && echo "⚠️ ADMIN POLICY ATTACHED"
done
🔑 Secrets & Configuration Security
Environment variable vs Secrets Manager — comparison
| Method | Visible to IAM users with Lambda read? | Supports rotation? | Audit trail? | Recommendation |
|---|---|---|---|---|
| Env variable (plaintext) | Yes — anyone with GetFunctionConfiguration | No | No | Never for secrets |
| Env variable (KMS encrypted) | Partial — encrypted but key access required | No | No | Acceptable for non-critical |
| AWS Secrets Manager | No — requires separate IAM permission | Yes | Yes (CloudTrail) | Recommended |
| SSM Parameter Store (SecureString) | No — requires separate IAM permission | Manual | Yes (CloudTrail) | Good for non-rotating secrets |
Secure secrets access pattern in Lambda
import boto3
import json
import os
from functools import lru_cache
# GOOD: Fetch secret once and cache (avoids per-request latency)
@lru_cache(maxsize=1)
def get_db_credentials():
client = boto3.client('secretsmanager', region_name='ap-south-1')
response = client.get_secret_value(SecretId=os.environ['DB_SECRET_ARN'])
return json.loads(response['SecretString'])
def lambda_handler(event, context):
creds = get_db_credentials()
db_password = creds['password']
# Use creds - never log them, never put in response body
# BAD - never do this:
# print(f"Connecting with password: {db_password}")
# return {"password": db_password}
📦 Supply Chain Security for Serverless
Dependency hash pinning
# requirements.txt — BAD: version only
boto3==1.28.0 # Can be replaced with compromised build
# requirements.txt — GOOD: version + hash
boto3==1.28.0 \
--hash=sha256:abc123def456... # pip will verify before installing
# Generate hashes automatically:
pip-compile --generate-hashes requirements.in > requirements.txt
# Verify existing requirements:
pip install --require-hashes -r requirements.txt
# For Node.js: use package-lock.json with integrity field
# npm ci # Uses lockfile with integrity hashes — safer than npm install
Common serverless supply chain attack types
| Attack Type | Example | Protection |
|---|---|---|
| Typosquatting | requsets instead of requests — malicious lookalike package | Hash pinning; private package mirror; Dependabot scanning |
| Namespace confusion | Private package company-utils served by public PyPI company-utils with higher version | Private registry; block public package names matching internal ones |
| Compromised maintainer account | Legitimate package updated with malicious code by hijacked maintainer | Hash pinning prevents updates; SBOM tracks exact versions |
| Malicious Lambda Layer | Third-party shared Layer contains backdoor — affects all functions using it | Only use Layers you publish; verify Layer source; pin Layer version ARN |
| CI/CD pipeline injection | Build pipeline compromised → malicious code added before deployment | Signed commits; artifact signing; CI/CD pipeline access controls |
🔍 Detection & Hardening Checklist
Enable for all Lambda functions
CloudWatch Logs enabled for all Lambda functions
Logs are essential — without them, Lambda is a black box during incidents. Minimum retention: 90 days.
Lambda function URL / API Gateway with authentication
No public unauthenticated Lambda endpoints unless deliberately intended. Use IAM auth or API keys.
Resource-based policy reviewed on all Lambdas
Lambda resource policies control who can invoke the function. Review for * principals.
No * in execution role permissions
Every Lambda should have minimal IAM role. Audit with aws iam simulate-principal-policy.
Secrets in Secrets Manager, not env variables
Rotate all secrets currently in env vars to Secrets Manager.
Lambda deployed inside VPC where it accesses private resources
VPC Lambda with private subnets prevents unexpected internet connectivity.
Concurrent execution limits set
Prevent runaway Lambda costs from abuse or DDoS. Set reserved concurrency.
Dead letter queue configured
Failed invocations captured for investigation — important for security event reconstruction.
Code signing enforced
Lambda code signing prevents deployment of unsigned or tampered packages.
GuardDuty Lambda Protection enabled
Runtime threat detection inside Lambda execution environment.