Back to Cheat Sheets

⚙️ Insecure Serverless Deployment Configuration

OWASP Serverless Top 10 - SAS-3

HIGH RISK

📋 What Is It?

Insecure Serverless Deployment Configuration occurs when the many settings that govern functions - and the buckets, queues, topics, tables, roles, and API Gateway routes around them - are left at insecure defaults or actively misconfigured. This weakness lives in the deployment artifact: the serverless.yml, SAM/CloudFormation template, Terraform module, and resource-based policies. When those grant public access, wildcard principals, plaintext secrets, or missing encryption, the vulnerability is baked in before the first request arrives. With no server to harden, the configuration is the security boundary - get it wrong and there is no second wall.

SAS-3 OWASP Rank
IaC Where It Lives
Easy To Detect

⚠️ Common Exploits

  • Public Buckets: public-read/public-write storage without Block Public Access
  • Wildcard Policies: Principal: "*" on Lambda/SNS/SQS/API resource policies
  • No-Auth Function URLs: AuthType: NONE exposes anonymous invoke
  • Plaintext Secrets: DB_PASSWORD in a cleartext environment variable
  • Missing Encryption: no KMS at rest, no aws:SecureTransport deny
  • Unrestricted CORS: Access-Control-Allow-Origin: * with credentials
  • No Throttling: public endpoints with no quotas - cost and DoS lever

🔴 Attack Flow

1. Enumerate: guess bucket names, Function URLs, API stages, ARNs
↓
2. Probe policy: test anonymous read/write/invoke
↓
3. Read the resource policy for Principal: "*"
↓
4. Exploit: pull data, invoke logic, read env-var secrets
↓
5. IMPACT: Pivot across the account via over-broad roles!

❌ Vulnerable Code

# Bad: over-broad role, plaintext secrets, no-auth URL, public bucket (serverless.yml) provider: name: aws iam: role: statements: - Effect: Allow Action: "*" ← over-broad execution role Resource: "*" environment: DB_PASSWORD: "S3cr3t-Pa55w0rd" ← plaintext secret in env var STRIPE_KEY: "sk_live_51H..." functions: process: handler: handler.process url: cors: true # reflects any origin # authorizer omitted → AuthType: NONE (anonymous invoke) resources: Resources: UploadsBucket: Type: AWS::S3::Bucket Properties: AccessControl: PublicRead ← public bucket, no Block Public Access // Bad: resource policy open to the whole world (JSON) { "Effect": "Allow", "Principal": "*", "Action": "lambda:InvokeFunction", "Resource": "arn:aws:lambda:us-east-1:123:function:process-orders" }

✅ Secure Code

# Good: least privilege, secret ARNs, IAM-auth URL, private bucket (serverless.yml) provider: name: aws environment: DB_SECRET_ARN: ${ssm:/prod/db/secret-arn} # reference, resolved at runtime plugins: - serverless-iam-roles-per-function functions: process: handler: handler.process iamRoleStatements: - Effect: Allow Action: ["s3:GetObject"] Resource: "arn:aws:s3:::orders-uploads/incoming/*" - Effect: Allow Action: ["secretsmanager:GetSecretValue"] Resource: ${ssm:/prod/db/secret-arn} url: authorizer: aws_iam # AuthType: AWS_IAM, not NONE cors: allowedOrigins: ["https://app.example.com"] # explicit allow-list allowCredentials: false resources: Resources: UploadsBucket: Type: AWS::S3::Bucket Properties: PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: { SSEAlgorithm: aws:kms } // Good: named principal + condition, and a TLS deny (JSON) { "Sid": "DenyInsecureTransport", "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": "arn:aws:s3:::orders-uploads/*", "Condition": { "Bool": { "aws:SecureTransport": "false" } } }

✓ Prevention Checklist

  • Enable S3 Block Public Access at account and bucket level
  • Scope every resource policy to a named principal + condition
  • Never use Principal: "*" on any resource policy
  • Require AuthType: AWS_IAM on Function URLs (or none at all)
  • Move secrets to Secrets Manager / SSM; reference ARNs only
  • Encrypt env vars and data at rest with a customer-managed KMS key
  • Enforce TLS with an aws:SecureTransport deny
  • Set throttling, quotas, and reserved concurrency on public paths
  • Restrict CORS to an explicit origin allow-list
  • Remove unnecessary triggers to shrink the invocation surface
  • Scope both execution and deploy roles to least privilege
  • Scan IaC on every pull request and fail the build on findings

🔍 Detection & Tools

IaC & Policy Scanners:

Checkov cfn-nag tfsec cfn-lint IAM Access Analyzer

Secrets & Runtime:

Secrets Manager SSM Parameter Store AWS KMS gitleaks CloudTrail

How to Test:

  • Enumerate buckets by naming scheme; try --no-sign-request
  • Probe Function URLs and API stages for anonymous invoke
  • Inspect resource policies for wildcard principals
  • Read get-function-configuration for plaintext secrets
  • Test CORS with a cross-origin request + credentials
  • Check for aws:SecureTransport deny and KMS encryption

🌍 Real-World

These are documented incident classes - recurring patterns, not specific CVEs.

  • Public cloud storage buckets: a long, well-documented class where buckets staging function uploads, exports, and backups allowed public read; providers later added Block Public Access defaults and console warnings in direct response.
  • Publicly exposed serverless endpoints: functions fronted by no-auth Function URLs or wildcard-principal policies, invoking business logic anonymously - doubling as DoS and cost-amplification surfaces when throttling was also absent.
  • Secrets leaked through function configuration: database passwords and API keys stored as plaintext env vars, readable by any over-broad role or recon tool, enabling lateral movement.
  • Chained misconfiguration: a public-write ingest bucket plus an ObjectCreated trigger plus a plaintext env-var credential = a breach with no application code exploit required.

📌 Quick Tips

  • DO NOT ship a public bucket or Principal: "*"
  • DO NOT leave a Function URL at AuthType: NONE
  • DO NOT put secrets in plaintext env vars
  • DO make every resource private by default
  • DO scan IaC on every change and fail the build
  • DO encrypt at rest and enforce TLS in transit
  • DO scope both execution and deploy roles tightly

📜 Compliance

Related Standards:

  • OWASP Serverless Top 10 - SAS-3
  • CWE-16 Configuration
  • CWE-732 Incorrect Permission Assignment
  • CWE-311 Missing Encryption of Sensitive Data
  • CIS AWS Foundations Benchmark