Skip to content

Serverless / FaaS Security Cheat Sheet

Introduction

Serverless computing (Functions as a Service — FaaS) platforms such as AWS Lambda, Azure Functions, and Google Cloud Functions simplify application development and scaling. However, the execution model (short-lived, event-driven functions running in managed environments) introduces unique security risks compared to traditional architectures.

This cheat sheet provides best practices to secure serverless applications and minimize attack surfaces.

Key Risks

  • Over-permissioned functions (broad IAM roles, * policies).
  • Unvalidated event inputs (API Gateway, S3, Pub/Sub, IoT).
  • Cold start data leakage (persistent state, side-channel timing).
  • Function chaining abuse (compromised function invoking others).
  • Shared environment risks (multi-tenant leakage, /tmp reuse).
  • Hardcoded secrets in code or env vars.
  • Excessive network access.

Best Practices

1. Principle of Least Privilege

  • Assign minimal IAM permissions to each function.
  • Use role-per-function (avoid shared high-privilege roles).
  • Scope database/API keys to the smallest set of actions needed.

Bad IAM Policy (too broad):

{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

Good IAM Policy (scoped):

{
  "Effect": "Allow",
  "Action": ["dynamodb:GetItem", "dynamodb:PutItem"],
  "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/Orders"
}

2. Environment Isolation

  • Disable default network access unless required (e.g. outbound internet access).
  • Place functions in private subnets with controlled egress.
  • Isolate sensitive functions (e.g. payment, auth) from general-purpose ones.
  • Separate production vs. staging environments with strict boundaries.

AWS Lambda VPC Config (restrictive):

VpcConfig:
  SubnetIds:
    - subnet-123456
  SecurityGroupIds:
    - sg-restrict-outbound

3. Secure Function Invocation

  • Enforce authentication and authorization on all triggers (API Gateway, Pub/Sub, S3, IoT).
  • Validate function-to-function calls with signed tokens or workload identities.
  • Apply rate limiting and throttling to mitigate DoS and abuse.

API Gateway Authorizer Example (JWT validation):

{
  "Type": "JWT",
  "IdentitySource": "$request.header.Authorization",
  "Issuer": "https://secure-idp.example.com/",
  "Audience": "my-api-client"
}

4. Event Data Validation

  • Treat all event payloads as untrusted input.
  • Apply strong input validation & sanitization (length, type, format).
  • Protect against common injection attacks (SQLi, XSS, JSON injection, deserialization).
  • Strip unnecessary fields and metadata before processing.

Example: Python input validation for Lambda

import json
import re

def lambda_handler(event, context):
    body = json.loads(event["body"])
    email = body.get("email", "")

    if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
        return {"statusCode": 400, "body": "Invalid email"}

    # process safely
    return {"statusCode": 200, "body": "OK"}

5. Cold Start & Execution Context Security

  • Do not assume function runtime context is clean between invocations.
  • Avoid storing secrets or temporary sensitive data in global/static variables.
  • Protect against side-channel leaks (timing differences, leftover files in /tmp).
  • For sensitive workloads, enforce single-use execution environments if the platform supports it.

Bad:

# Secret stays in global variable across invocations
SECRET_KEY = "hardcoded-secret"

Good:

import os
from my_secrets_lib import get_secret

def lambda_handler(event, context):
    secret = get_secret("db-password")  # fetch fresh each time
    ...

6. Secrets Management

  • Avoid storing secrets in environment variables.
  • Fetch secrets from secure stores (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager).
  • Use ephemeral credentials (STS, workload identity federation).
  • Rotate secrets automatically.

AWS Lambda Secret Fetch (Python Boto3):

import boto3, json

def get_secret(secret_name):
    client = boto3.client("secretsmanager")
    response = client.get_secret_value(SecretId=secret_name)
    return json.loads(response["SecretString"])

7. Monitoring & Logging

  • Use centralized logging (CloudWatch, Azure Monitor, GCP Logging).
  • Mask secrets and PII.

Example: Redacting fields

import logging

def log_event(event):
    safe_event = {k: ("***" if "password" in k else v) for k,v in event.items()}
    logging.info(safe_event)

8. Supply Chain Security

  • Scan dependencies (npm audit, pip-audit, safety).
  • Use minimal deployment packages.
  • Sign packages with checksums.

AWS Lambda Layer Hash Validation:

shasum -a 256 layer.zip

Do’s and Don’ts

Do:

  • Enforce least privilege per function.
  • Validate all event inputs.
  • Fetch secrets from vaults, not env vars.
  • Restrict network egress.
  • Monitor invocations and logs.

Don’t:

  • Hardcode secrets in code or configs.
  • Assume clean runtime between invocations.
  • Give * IAM permissions.
  • Leave sensitive data in /tmp or globals.
  • Trust event sources blindly.

References