Serverless Security
Serverless architectures eliminate server management but introduce new challenges: function-level permissions, event injection, dependency vulnerabilities, and an expanded attack surface.
Shared Responsibility Model
Provider (AWS/Azure/GCP)
- Physical security and infrastructure
- Runtime environment
- Isolation between functions
- Operating system patching
Customer (You)
- Function code
- Dependencies and libraries
- IAM permissions
- Data encryption
- API Gateway configuration
- Logging and monitoring
OWASP Serverless Top 10
- Injection Flaws: SQLi, command injection in event data
- Broken Authentication: Token mismanagement, weak authn
- Sensitive Data Exposure: Hardcoded secrets, verbose logs
- XML External Entities (XXE): Insecure XML parsing
- Broken Access Control: Overly permissive IAM
- Security Misconfiguration: Default configs, open ports
- Cross-Site Scripting (XSS): Inadequate output encoding
- Insecure Deserialization: Deserialization of untrusted events
- Using Components with Known Vulnerabilities: Outdated deps
- Insufficient Logging: Lack of an audit trail
IAM and Least Privilege
Each function should have a dedicated IAM role with the minimum permissions required.
AWS Lambda - IAM Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789:table/MyTable"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
IAM Principles
- Function-specific roles: Never share roles between functions
- Resource-level permissions: Specify exact ARNs, not wildcards
- Time-based access: Use AWS STS for temporary credentials
- Deny by default: Explicitly allow only what is necessary
Secrets Management
- AWS Secrets Manager: Automatic rotation, encryption at rest
- Azure Key Vault: Managed HSM, access policies
- Environment variables encryption: KMS to encrypt env vars
- Parameter Store: AWS SSM Parameter Store for configs
- Never hardcode: Secrets in code or repositories
AWS Lambda + Secrets Manager Example
import boto3
import json
def lambda_handler(event, context):
# Fetch secret from Secrets Manager
session = boto3.session.Session()
client = session.client(service_name='secretsmanager')
get_secret_value_response = client.get_secret_value(
SecretId='prod/db/password'
)
secret = json.loads(get_secret_value_response['SecretString'])
db_password = secret['password']
# Use the password to connect to the DB
# ...
Input Validation and Sanitization
Events from API Gateway, S3, DynamoDB Streams, etc. must be validated rigorously:
// Node.js Lambda example
import Joi from 'joi';
const schema = Joi.object({
userId: Joi.string().uuid().required(),
action: Joi.string().valid('create', 'update', 'delete').required(),
data: Joi.object().required()
});
export const handler = async (event) => {
try {
const body = JSON.parse(event.body);
const { error, value } = schema.validate(body);
if (error) {
return {
statusCode: 400,
body: JSON.stringify({ error: error.details })
};
}
// Process validated input
// ...
} catch (e) {
console.error('Validation error:', e);
return { statusCode: 400, body: 'Invalid input' };
}
};
Dependency Management
- SCA tools: Snyk, npm audit, Dependabot
- Minimal dependencies: Reduce the attack surface
- Lock files: package-lock.json, yarn.lock for reproducibility
- Private registries: Host internally approved deps
- SBOM: Software Bill of Materials for auditability
Timeout and Resource Limits
# AWS Lambda configuration
Function:
Type: AWS::Serverless::Function
Properties:
Timeout: 30 # seconds (default 3s, max 900s)
MemorySize: 512 # MB
ReservedConcurrentExecutions: 100 # limit concurrency
Environment:
Variables:
MAX_RETRY_ATTEMPTS: 3
CONNECTION_TIMEOUT: 5000
VPC Configuration
Functions that access private resources should run in a VPC with appropriate security groups:
- Private subnets: Functions with no direct internet access
- NAT Gateway: For outbound internet access if needed
- Security groups: Whitelisting of ports and IPs
- VPC Endpoints: Private access to AWS services (S3, DynamoDB)
Logging and Monitoring
- CloudWatch Logs: Centralize logs from all functions
- CloudTrail: Audit trail of invocations and changes
- X-Ray: Distributed tracing for troubleshooting
- Custom metrics: Business logic metrics via CloudWatch
- Alerting: Alarms on errors, timeouts, throttles
Structured Logging
import { Logger } from '@aws-lambda-powertools/logger';
const logger = new Logger({ serviceName: 'userService' });
export const handler = async (event, context) => {
logger.addContext(context);
logger.info('Processing request', {
userId: event.userId,
requestId: context.requestId
});
try {
// Business logic
} catch (error) {
logger.error('Processing failed', { error });
throw error;
}
};
API Gateway Security
- Authentication: Cognito, API Keys, Lambda Authorizers
- Rate limiting: Usage plans and throttling
- WAF integration: AWS WAF for protection against the OWASP Top 10
- Request validation: Models and validators in API Gateway
- CORS: Configure allowed origins
Cold Start Security
Cold starts can be exploited for timing attacks. Mitigations:
- Provisioned concurrency for critical functions
- Minimize package size for fast startup
- Lazy load heavy dependencies
- Warm-up schedules via CloudWatch Events
Runtime Security
- PureSec: Runtime protection for serverless
- Protego: Serverless security platform
- Twistlock: Prisma Cloud for serverless
- Snyk: Vulnerability scanning integrated into CI/CD
Final Recommendations
Serverless does not mean "no security". Implement least privilege IAM, validate all inputs, manage secrets appropriately, and monitor comprehensively. Use IaC (Serverless Framework, SAM) for consistency and review. Integrate security scanning into CI/CD. Serverless expands the attack surface - every event source and integration point is a potential vector.
