Serverless Security - Sicherheit in FaaS-Umgebungen
Serverless Security bezeichnet die Absicherung von Function-as-a-Service (FaaS) Umgebungen wie AWS Lambda, Azure Functions und Google Cloud Functions. Angriffsvektoren: Event-Injection, überprivilegierte Rollen, unsichere Abhängigkeiten, Function-Level-Broken-Access-Control, Cold-Start-Timing-Angriffe und zu lange Timeouts. Schutz: OWASP Serverless Top 10, minimale IAM-Rechte, Layer-Validierung, Dependency-Scanning.
Serverless Security bezeichnet die Absicherung von Function-as-a-Service (FaaS) Umgebungen, in denen Code ohne Serververwaltung in kurzlebigen Containern ausgeführt wird. Das Serverless-Modell verändert die Angriffsfläche grundlegend: Kein OS-Patching, aber neue Risiken durch Event-basierte Trigger, IAM-Fehlkonfigurationen und die Komplexität vieler kleiner Funktionen.
Serverless Angriffsfläche
Typische Serverless-Architektur (AWS Lambda)
Eine typische Serverless-Architektur verbindet verschiedene Event-Quellen mit Lambda-Funktionen und nachgelagerten Services, beispielsweise:
- API Gateway → Lambda-Funktion → DynamoDB
- S3-Upload → Lambda-Funktion → SQS-Queue
- IoT-Event → Lambda-Funktion → RDS
- SNS-Message → Lambda-Funktion → SES (E-Mail)
Angriffsvektoren
- Event Injection: Bösartiger Input via API Gateway, S3 oder SQS
- IAM-Fehlkonfiguration: Lambda-Rolle mit zu vielen Rechten
- Unsichere Dependencies: npm/pip-Pakete mit bekannten Schwachstellen
- Secrets in Env-Vars: Zugangsdaten direkt in der Funktion hinterlegt
- Verbose Logging: Sensible Daten in CloudWatch-Logs
- ZIP-Path-Traversal: Manipulation des Deployment-Packages
OWASP Serverless Top 10
| ID | Bezeichnung |
|---|---|
| SAS-1 | Function Event-Data Injection |
| SAS-2 | Broken Authentication |
| SAS-3 | Insecure Serverless Deployment Configuration |
| SAS-4 | Over-Privileged Function Permissions and Roles |
| SAS-5 | Inadequate Function Monitoring and Logging |
| SAS-6 | Insecure Third-Party Dependencies |
| SAS-7 | Insecure Application Secrets Storage |
| SAS-8 | Denial of Service via Reserved Concurrency |
| SAS-9 | Serverless Business Logic Manipulation |
| SAS-10 | Improper Exception Handling and Verbose Error Messages |
SAS-1: Event-Data-Injection
Der Angriff nutzt aus, dass Lambda-Funktionen Daten aus externen Event-Quellen oft ungeprüft verarbeiten. Ein Angreifer sendet an API Gateway manipulierte Eingaben, die z.B. SQL-Injection enthalten:
POST /api/users
{"userId": "1 OR 1=1--"}
Eine unsichere Lambda-Funktion (Python) würde den Input direkt in die Query einbauen:
def handler(event, context):
user_id = event['body']['userId']
query = f"SELECT * FROM users WHERE id = {user_id}"
# → SQL-Injection!
result = db.execute(query)
Die sichere Version verwendet parameterisierte Queries:
def handler(event, context):
user_id = event['body']['userId']
query = "SELECT * FROM users WHERE id = %s"
result = db.execute(query, (user_id,))
Weitere Injektionsvektoren sind S3-Event-Trigger (bösartige Dateinamen wie ../../../../etc/passwd, die Path-Traversal-Operationen auslösen - Schutz: os.path.basename()) und SNS/SQS-Nachrichten, bei denen bei SSRF in einem anderen Service eine interne Event-Injektion möglich ist.
SAS-4: Überprivilegierte Rollen
Ein häufig gesehenes problematisches Beispiel:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]
}
Diese Konfiguration gibt der Lambda-Funktion vollen AWS-Admin-Zugriff. Bei einer RCE-Schwachstelle in der Funktion ist damit das komplette AWS-Konto kompromittiert. Der Angriff verläuft typischerweise so: Eine Injection-Schwachstelle wird gefunden, dann wird der AWS Metadata-Service abgefragt (bei IMDSv1 über http://169.254.169.254/latest/meta-data/iam/security-credentials/), worüber temporäre AWS Credentials der Lambda-Rolle extrahiert werden. Mit diesen Credentials können dann S3-Buckets geleert, EC2-Instanzen gestartet und weitere Ressourcen missbraucht werden.
Least-Privilege Beispiel
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:PutItem"],
"Resource": "arn:aws:dynamodb:eu-west-1:123456789:table/users"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/uploads/*"
}
]
}
Das Tool IAMzero analysiert CloudTrail-Logs, erkennt welche Rechte tatsächlich genutzt werden, und generiert daraus eine minimale IAM-Policy.
AWS-Empfehlung: IMDSv2 enforzen, um SSRF auf den Metadata-Service zu verhindern:
aws lambda put-function-configuration \
--function-name my-function \
--description "IMDSv2 required"
# Auch: Instance-Metadata-Hop-Limit = 1 in Lambda-Config
SAS-7: Secrets-Management
Umgebungsvariablen in Lambda sind nur Base64-codiert, nicht verschlüsselt. Konsolen-Zugriff auf Lambda → Configuration zeigt Passwörter im Klartext. CloudFormation-Templates, die in Git landen, exponieren Secrets für alle mit Repository-Zugriff.
Ein schlechtes Muster ist das Hardcoden von Secrets:
def handler(event, context):
DB_PASSWORD = "geheimespasswort123" # Im Source-Code!
API_KEY = os.environ['API_KEY'] # Sichtbar in Lambda-Console!
Gutes Muster 1 - AWS Secrets Manager:
import boto3
_cached_secret = None
def get_secret(secret_name):
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
def handler(event, context):
global _cached_secret
if _cached_secret is None:
_cached_secret = get_secret('myapp/production/database')
db_password = _cached_secret['password']
Das Caching beim Cold Start ist wichtig, damit nicht bei jedem Funktionsaufruf ein API-Call an Secrets Manager erfolgt.
Gutes Muster 2 - AWS Parameter Store (günstiger):
ssm = boto3.client('ssm')
password = ssm.get_parameter(
Name='/myapp/database/password',
WithDecryption=True # KMS-verschlüsselt!
)['Parameter']['Value']
Mittelmäßig - Encrypted Environment Variables: KMS-Key für Lambda konfigurieren verschlüsselt Env-Vars im Ruhezustand, sie sind jedoch im Klartext sichtbar, wenn die Funktion ausgeführt wird.
Denial-of-Service via Reserved Concurrency
AWS Lambda hat standardmäßig ein Limit von 1000 simultanen Ausführungen pro Account (erhöhbar). Wird dieses Limit erreicht, werden alle anderen Lambdas im Account throttled. Ein Angreifer kann durch Senden von 1000+ parallelen Requests an eine Funktion ohne Concurrency-Limit einen “Account-weiten DoS” auslösen, bei dem alle anderen Dienste offline gehen.
Schutzmaßnahmen
# Reserved Concurrency setzen:
aws lambda put-function-concurrency \
--function-name api-handler \
--reserved-concurrent-executions 100
Zusätzlich helfen Provisioned Concurrency für kritische Funktionen, API Gateway Throttling-Limits (z.B. 1000 RPS, Burst 5000), und eine WAF vor dem API Gateway für Rate-Limiting pro IP.
Monitoring und Audit
CloudWatch und Lambda Insights
Metriken wie Duration, Memory, Cold Starts und Error Rate überwachen. Alarme bei ungewöhnlichen Mustern einrichten, z.B. Duration-Spikes als Indiz für einen laufenden Angriff.
CloudTrail
Lambda:CreateFunction und Lambda:UpdateFunctionCode überwachen: Wer deployed? Lambda:InvokeFunction: Ungewöhnliche Aufrufer?
Powertools for Lambda (AWS)
from aws_lambda_powertools import Logger, Tracer, Metrics
logger = Logger(service="api-handler")
tracer = Tracer(service="api-handler")
@logger.inject_lambda_context(log_event=True)
@tracer.capture_lambda_handler
def handler(event, context):
user_id = event.get('requestContext', {}).get('authorizer', {}).get('principalId')
logger.info("Processing request", extra={"user_id": user_id})
# Structured logs → CloudWatch → SIEM
Falco für Serverless (Runtime Security): Erkennt unerwartete Syscalls und Netzwerkverbindungen aus Lambda, Integration über Lambda-Layer.
SBOM für Dependencies: cyclonedx-python für Python-Lambdas, @cyclonedx/cyclonedx-npm für Node.js-Lambdas - automatisch bei jedem Deploy scannen (Snyk, OWASP Dependency-Check).
Serverless Security Checkliste
IAM
- Least-Privilege-Rolle pro Funktion (nie AdministratorAccess!)
- IAMzero oder Access Analyzer nutzen um ungenutzte Rechte zu finden
- Keine Wildcard-Actions (
*) in IAM-Policies - Kein
Resource: "*"wenn spezifische ARNs möglich sind - IMDSv2 enforzen (Token-basiert, verhindert SSRF)
Secrets
- Kein Secret in Environment Variables im Klartext
- Kein Secret im Source Code oder CloudFormation-Template
- Secrets Manager oder Parameter Store (SecureString) verwenden
- Secrets regelmäßig rotieren (Secrets Manager kann automatisch rotieren)
Code-Sicherheit
- Input-Validierung aller Event-Daten (API Gateway Events, S3 Events etc.)
- Parameterized Queries für DB-Zugriffe
- Dependency-Scanning (Snyk, npm audit) in CI/CD
- SAST im Pull-Request-Workflow
Deployment
- ZIP-Integrität prüfen (Code-Signing für Lambda aktivieren)
- VPC-Konfiguration wenn DB-Zugriff nötig (nicht öffentlich!)
- Reserved Concurrency setzen (DoS-Schutz)
- API Gateway WAF aktivieren (AWS WAF)
Monitoring
- CloudTrail in allen Regionen (Lambda-Management-Events)
- Alarme für Fehlerrate, Duration, Throttling und Cold-Start-Spikes
- Structured Logging ohne PII in Logs
- Dead Letter Queue (DLQ) für fehlgeschlagene Invocations konfigurieren