Serverless computing hands infrastructure management to the cloud provider. You write a function, attach a trigger, and the provider handles scaling, patching, and availability. The security boundary moves but doesn’t disappear. The provider secures the runtime and the host OS; your code, your IAM policies, and your event source configurations are still yours to get wrong.
The
\[earlier cloud security overview\](/posts/2023-04-21-cloud-security-best-practices-and-common-vulnerabilities/) covered the general picture. This one narrows in on the serverless-specific misconfigurations that keep showing up on engagements and in public breach reports.
Authentication and authorization in serverless#
IAM is the perimeter in serverless. There’s no network firewall sitting between your function and the S3 bucket it reads from. The execution role attached to the function is the access control, so over-permissioned roles are the most common finding.
Here’s a CloudFormation execution role scoped to the minimum a function needs: CloudWatch log writes and read access to one specific S3 bucket.
Resources:
MyLambdaFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: MyLambdaFunctionPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- s3:GetObject
Resource: arn:aws:s3:::my-bucket/*Compare that with the wildcard Action: "*" and Resource: "*" roles that show up in starter templates and never get tightened. If an attacker finds an SSRF or injection flaw in the function, the execution role is exactly what they inherit.
On the API side, front your functions with a proper authorizer. Amazon Cognito, Microsoft Entra ID (formerly Azure Active Directory), or Google Cloud IAM all integrate directly with their respective API gateway services.
A Cognito-backed API Gateway in SAM:
Resources:
MyApiGateway:
Type: AWS::Serverless::Api
Properties:
Auth:
DefaultAuthorizer: MyCognitoAuthorizer
Authorizers:
MyCognitoAuthorizer:
UserPoolArn: !GetAtt MyCognitoUserPool.Arn
StageName: prod
MyCognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: MyUserPoolEvery request to this API authenticates against the Cognito User Pool before the Lambda function ever executes. Unauthenticated HTTP endpoints exposed directly to the internet are how you get unauthorized invocations and, in the worst case, data exfiltration through the function’s execution role.
Vulnerability management#
Serverless functions still pull in dependencies, and those dependencies still have CVEs. The deployment package might be smaller than a full container image, but that just means the blast radius per vulnerable library is more concentrated.
Scan dependencies before deployment. Snyk
covers Node.js, Python, Java, Go, and .NET dependency trees; run snyk test to check and snyk fix to apply patches (the older snyk wizard command was deprecated). Mend
(formerly WhiteSource, rebranded 2022) provides SCA with license compliance. Trivy
from Aqua Security scans container images, filesystem paths, and IaC templates in one tool.
For static analysis of your own code, SonarQube and Checkmarx remain the established options. On the dynamic testing side, OWASP ZAP and Burp Suite are the workhorses. (Arachni, sometimes still listed in older guides, has been effectively abandoned since 2017.)
# Scan a Node.js project with Snyk
snyk test
# Auto-fix where patches exist
snyk fixMonitoring and logging#
CloudWatch (AWS), Azure Monitor, and Cloud Logging (GCP) provide baseline function-level metrics: invocation count, duration, error rate, throttles. That baseline catches runaway loops and crash spikes, but it won’t tell you that a function is being invoked with crafted payloads or that someone is enumerating your API.
Layer application-level logging on top. Log the event source, the caller identity (if available from the authorizer context), and enough of the request to reconstruct what happened without logging sensitive data. Ship those logs to a SIEM or a centralized log aggregation service where you can correlate across functions.
Datadog, Lumigo, and AWS X-Ray all provide distributed tracing purpose-built for serverless. Tracing matters here because a single user action can fan out across multiple functions, queues, and databases, and reconstructing that chain from raw CloudWatch logs alone is painful.
Store secrets (API keys, database credentials, signing keys) in your provider’s secrets service (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) and reference them at runtime. Never hardcode secrets in environment variables baked into the deployment template. A leaked CloudFormation template with DASHBIRD_API_KEY: sk-live-abc123 in the Environment block is a credential exposure.
Serverless misconfigurations#
Misconfigurations split into three layers. Thinking about them by layer helps during both hardening and assessment.
Function-level#
- Over-permissioned execution roles. A function that reads from DynamoDB does not need
dynamodb:*; it needsdynamodb:GetItemon specific table ARNs. - Overly permissive CORS policies. An API Gateway returning
Access-Control-Allow-Origin: *lets any origin make authenticated requests against it. Scope allowed origins to the domains that actually need access. - Unauthenticated triggers. An HTTP-triggered function with no authorizer is reachable by anyone who finds the endpoint URL. Public endpoints are sometimes intentional (webhooks, health checks), but they should be a deliberate choice, not a default.
- Excessive timeout and memory. Setting a function’s timeout to 15 minutes and memory to 10 GB when it needs 3 seconds and 128 MB creates a denial-of-wallet attack surface: an attacker who can trigger the function repeatedly can run up your bill.
Resource-level#
- Publicly accessible storage buckets. S3 bucket policy reviews are the single highest-value check in any AWS assessment. Capital One’s 2019 breach started with an SSRF vulnerability in a misconfigured WAF that let the attacker reach the EC2 metadata service, pivot to IAM credentials, and exfiltrate over 100 million customer records from S3. The root cause was a chain of misconfigurations, not a single mistake, but an overly permissive role on the WAF’s EC2 instance is what made the S3 data reachable.
- Unencrypted data at rest. Enable server-side encryption on S3 buckets, DynamoDB tables, and RDS instances. AWS KMS, Azure Key Vault, and Google Cloud KMS all support customer-managed keys for tighter control.
- Insecure queue and topic policies. SQS queues, SNS topics, and Pub/Sub subscriptions need resource policies that restrict who can publish and subscribe. A queue with a wildcard principal policy is an open relay.
Infrastructure-level#
- Unrestricted security groups. Even in serverless architectures, functions that run inside a VPC (for database access, for example) inherit the VPC’s security group rules. Review those groups.
- Weak IAM policies at the account level. Service control policies (SCPs) in AWS Organizations and Azure Policy definitions set guardrails that individual function roles can’t exceed. If the guardrails are missing, any single function role misconfiguration can escalate.
- Insecure CI/CD pipelines. The deployment pipeline has write access to your production environment. If the pipeline’s credentials are over-scoped or the build artifact isn’t verified, a compromised pipeline deploys attacker code directly into your functions.
Serverless-specific attack surfaces#
Two attack classes show up in serverless that don’t map cleanly to traditional infrastructure.
Event injection#
Serverless functions get triggered by events: HTTP requests, queue messages, S3 object notifications, database stream records. If the function trusts event data without validation, an attacker who controls the event source (or poisons an upstream queue) can inject payloads. An S3 trigger that passes the object key directly into a shell command is command injection. A DynamoDB stream handler that evaluates record content as an expression is code injection. The OWASP Serverless Top 10 (2018) ranked injection as the top serverless risk specifically because of this expanded attack surface. Validate and sanitize event data the same way you would any untrusted input.
Denial-of-wallet#
Traditional DDoS aims to exhaust compute capacity. In a serverless model, the provider auto-scales to handle load, so the service stays up, but your bill scales with it. An attacker who can trigger a function 10 million times costs you money, not downtime. Set concurrency limits on functions, throttle at the API Gateway layer, and wire up budget alerts with automatic shutoff via CloudWatch alarms tied to Lambda billing metrics.
Detection and hardening tools#
Scan your infrastructure-as-code templates before deployment, not after. Catching a wildcard IAM policy in a pull request is cheaper than finding it in a production audit.
- Checkov (Prisma Cloud) scans Terraform, CloudFormation, Kubernetes manifests, and Dockerfiles against hundreds of built-in policies.
- KICS (Checkmarx) covers similar ground with a different policy engine and supports Ansible, Helm, and OpenAPI specs.
- Trivy combines IaC scanning with container image and dependency scanning in one binary.
For runtime auditing of live environments:
- Prowler audits AWS, Azure, and GCP against CIS Benchmarks, PCI-DSS, HIPAA, and other compliance frameworks. Originally AWS-only, it expanded to multi-cloud in v3.
- ScoutSuite (NCC Group) provides multi-cloud security posture assessment with a browser-based report.
- Amazon Macie uses machine learning to discover and classify sensitive data in S3. Enable it through the console or CLI:
# Enable Macie in the current region
aws macie2 enable-macie
# Create a classification job targeting a specific bucket
aws macie2 create-classification-job \
--job-type ONE_TIME \
--s3-job-definition '{"bucketDefinitions": [{"accountId": "123456789012", "buckets": ["my-bucket"]}]}'Macie flags buckets containing PII, credentials, or financial data and alerts on policy violations like public access or missing encryption.
Putting it together#
The principles are the same ones that apply everywhere else: least privilege, input validation, encryption, monitoring. What changes in serverless is where you apply them. IAM policies do the job that firewall rules used to. Every event source is an input you need to validate. And the thing denial-of-service attacks exhaust isn’t your CPU; it’s your budget.
Scan IaC before deployment. Audit live environments regularly. Keep execution roles narrow and review them on the same cadence you review code.