Migrating from Serverless Framework to AWS CDK: Part 5 - Authentication, Authorization, and IAM
Implement robust authentication with Cognito, API Gateway authorizers, and fine-grained IAM policies when migrating from Serverless Framework to AWS CDK.
Woche 9 unserer CDK-Migration. Alles lief reibungslos, bis unser Head of Security mit einer einzigen Frage in das morgendliche Standup kam: "Welche Lambda-Funktionen können auf Kundenzahlungsdaten zugreifen?"
Dreiundzwanzig Gesichter starrten ihn schweigend an. Unser Serverless Framework-Setup war über 18 Monate organisch gewachsen. Funktionen hatten "*"
IAM-Berechtigungen, weil es "schneller zu shippen war". Autorisierungslogik war über 12 verschiedene Custom Authorizer verstreut. Wir hatten keinen Audit-Trail, wer auf was zugreifen konnte.
Diese Frage löste ein 3-wöchiges Sicherheitsaudit aus, das 47 überprivilegierte Funktionen und eine potenzielle Compliance-Strafe von $180K aufdeckte. Das ist die Geschichte des Wiederaufbaus von Enterprise-Grade-Authentifizierung und -Autorisierung während einer Live-Migration - ohne eine einzige Benutzersitzung zu brechen.
Seriennavigation:
- Teil 1: Warum wechseln?
- Teil 2: CDK-Umgebung einrichten
- Teil 3: Migration von Lambda-Funktionen und API Gateway
- Teil 4: Datenbank- und Umgebungsmanagement
- Teil 5: Authentifizierung, Autorisierung und IAM (dieser Beitrag)
- Teil 6: Migrationsstrategien und Best Practices
Das Authentifizierungs-Albtraum-Audit#
Bevor wir etwas reparieren konnten, mussten wir verstehen, was wir hatten. Das Audit enthüllte unsere Authentifizierungshölle:
Die Serverless Framework-Realitätsprüfung#
Benutzerverwaltung: Drei verschiedene Cognito-Pools in verschiedenen Umgebungen, manuell erstellt, null Dokumentation der benutzerdefinierten Attribute.
Autorisierung: 12 verschiedene Lambda-Authorizer, jeder mit unterschiedlicher JWT-Validierungslogik, kein Caching, durchschnittlich 400ms Autorisierungslatenz.
IAM-Berechtigungen: 47 Lambda-Funktionen mit Wildcard-Berechtigungen. Unsere kritischste Zahlungsfunktion hatte "*"
Zugriff auf alle DynamoDB-Tabellen.
Secrets: API-Keys in Umgebungsvariablen hartcodiert, umgebungsübergreifend geteilt, zuletzt rotiert "irgendwann 2022".
Audit-Trail: Keiner. Null Protokollierung von Autorisierungsentscheidungen. Keine Möglichkeit zu beantworten "wer hat wann auf was zugegriffen".
Die Geschäftsauswirkung#
- Compliance-Risiko: $180K potenzielle GDPR-Strafe für zu breiten Datenzugriff
- Performance-Auswirkung: 400ms durchschnittliche Autorisierungslatenz (28% der Gesamtanfrage-Zeit)
- Betriebsaufwand: 3 Stunden/Woche zur Lösung von Authentifizierungsproblemen
- Sicherheitsschuld: 47 Funktionen mit unnötigen Berechtigungen
Produktionsreife Cognito-Implementierung#
Nach dem Audit bauten wir die Authentifizierung mit Enterprise-Kontrollen neu auf. Hier ist der kampferprobte Ansatz:
# serverless.yml
resources:
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: ${self:service}-${opt:stage}-users
Schema:
- Name: email
Required: true
Mutable: false
- Name: role
AttributeDataType: String
Mutable: true
AutoVerifiedAttributes:
- email
Policies:
PasswordPolicy:
MinimumLength: 8
RequireUppercase: true
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: ${self:service}-${opt:stage}-client
UserPoolId: !Ref UserPool
GenerateSecret: false
ExplicitAuthFlows:
- ALLOW_USER_PASSWORD_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
Der Enterprise-Grade CDK-Ansatz#
Hier ist die Cognito-Implementierung, die das SOC 2-Audit bestanden hat und 180K+ Benutzer verarbeitet:
// lib/constructs/auth/production-cognito.ts
import {
UserPool,
UserPoolClient,
AccountRecovery,
Mfa,
UserPoolOperation,
StringAttribute,
ClientAttributes,
OAuthScope,
UserPoolDomain,
CognitoUserPoolsAuthorizer
} from 'aws-cdk-lib/aws-cognito';
import { Duration, RemovalPolicy, Tags } from 'aws-cdk-lib';
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
import { Alarm, Metric, TreatMissingData } from 'aws-cdk-lib/aws-cloudwatch';
export class ProductionCognitoAuth extends Construct {
public readonly userPool: UserPool;
public readonly userPoolClient: UserPoolClient;
public readonly authorizer: CognitoUserPoolsAuthorizer;
constructor(scope: Construct, id: string, props: {
stage: string;
domainPrefix?: string;
callbackUrls?: string[];
api: RestApi;
}) {
super(scope, id);
// Benutzerpool mit audit-konformen Einstellungen erstellen
this.userPool = new UserPool(this, 'EnterpriseUserPool', {
userPoolName: `my-service-${props.stage}-users-v2`,
// Erweiterte Sicherheit: keine Selbstregistrierung in Produktion
selfSignUpEnabled: props.stage !== 'prod',
signInAliases: {
email: true,
username: false, // Nur E-Mail-Anmeldung reduziert Angriffsfläche
},
signInCaseSensitive: false,
autoVerify: { email: true },
// SOC 2-konforme Passwort-Richtlinie
passwordPolicy: {
minLength: 14, // Nach Audit von 12 erhöht
requireLowercase: true,
requireUppercase: true,
requireDigits: true,
requireSymbols: true,
tempPasswordValidity: Duration.hours(24), // Von 3 Tagen reduziert
},
// Umfassende Benutzerattribute für RBAC
standardAttributes: {
email: { required: true, mutable: false },
givenName: { required: true, mutable: true },
familyName: { required: true, mutable: true },
},
customAttributes: {
// Rollenbasierte Zugriffskontrolle
role: new StringAttribute({ mutable: true }),
department: new StringAttribute({ mutable: true }),
accessLevel: new StringAttribute({ mutable: true }),
// Audit-Trail-Attribute
lastLoginDate: new StringAttribute({ mutable: true }),
createdBy: new StringAttribute({ mutable: false }),
// Compliance-Attribute
dataAccessLevel: new StringAttribute({ mutable: true }),
complianceFlags: new StringAttribute({ mutable: true }),
},
// Enterprise-Sicherheitseinstellungen
accountRecovery: AccountRecovery.EMAIL_ONLY,
mfa: props.stage === 'prod' ? Mfa.REQUIRED : Mfa.OPTIONAL,
mfaSecondFactor: {
sms: false, // Nur TOTP für Sicherheit
otp: true,
},
// Erweiterte Bedrohungsschutz
advancedSecurityMode: props.stage === 'prod'
? AdvancedSecurityMode.ENFORCED
: AdvancedSecurityMode.AUDIT,
// E-Mail-Konfiguration für Marken-Kommunikation
emailSettings: {
from: 'noreply@yourcompany.com',
replyTo: 'support@yourcompany.com',
},
// Geräteverfolgung für Sicherheit
deviceTracking: {
challengeRequiredOnNewDevice: true,
deviceOnlyRememberedOnUserPrompt: false,
},
// Datenschutz
removalPolicy: props.stage === 'prod' ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY,
deletionProtection: props.stage === 'prod',
});
// Enterprise Lambda-Trigger hinzufügen
this.addSecurityTriggers(props.stage);
// Produktions-App-Client erstellen
this.userPoolClient = new UserPoolClient(this, 'EnterpriseClient', {
userPool: this.userPool,
userPoolClientName: `my-service-${props.stage}-client-v2`,
// Erlaubte Authentifizierungsflows
authFlows: {
userPassword: false, // Weniger sicheren Flow deaktivieren
userSrp: true, // Secure Remote Password-Protokoll
custom: true, // Benutzerdefinierte Auth-Challenges
adminUserPassword: props.stage !== 'prod', // Admin-Flow nur in Nicht-Prod
},
// OAuth-Konfiguration für Enterprise-SSO
oAuth: {
flows: {
authorizationCodeGrant: true,
implicitCodeGrant: false, // Implicit Flow für Sicherheit deaktivieren
clientCredentials: false,
},
scopes: [
OAuthScope.EMAIL,
OAuthScope.OPENID,
OAuthScope.PROFILE,
OAuthScope.custom('read:profile'),
OAuthScope.custom('write:profile'),
],
callbackUrls: props.callbackUrls || [],
logoutUrls: [`https://${props.stage === 'prod' ? 'app' : props.stage}.yourcompany.com/logout`],
},
generateSecret: false, // Öffentlicher Client für SPA
// Feinabgestimmter Attributzugriff
readAttributes: new ClientAttributes()
.withStandardAttributes({
email: true,
emailVerified: true,
givenName: true,
familyName: true,
})
.withCustomAttributes('role', 'department', 'accessLevel'),
writeAttributes: new ClientAttributes()
.withCustomAttributes('lastLoginDate'), // Begrenzter Schreibzugriff
// Sicherheitsfokussierte Token-Einstellungen
idTokenValidity: Duration.minutes(30), // Kurzlebig für Sicherheit
accessTokenValidity: Duration.minutes(30), // Kurzlebig für Sicherheit
refreshTokenValidity: Duration.days(1), // Tägliche Neauthentifizierung
// Erweiterte Sicherheitsoptionen
preventUserExistenceErrors: true,
enableTokenRevocation: true,
// Benutzerdefinierte Token-Einstellungen
authSessionValidity: Duration.minutes(3), // Schneller Auth-Flow-Timeout
});
// API Gateway-Authorizer erstellen
this.authorizer = new CognitoUserPoolsAuthorizer(this, 'CognitoAuthorizer', {
cognitoUserPools: [this.userPool],
authorizerName: `${props.api.restApiName}-cognito-auth`,
identitySource: 'method.request.header.Authorization',
resultsCacheTtl: Duration.minutes(5), // Cache für Performance
});
// Benutzerdefinierte Domain für Markenerlebnis hinzufügen
if (props.domainPrefix) {
new UserPoolDomain(this, 'UserPoolDomain', {
userPool: this.userPool,
cognitoDomainPrefix: `${props.domainPrefix}-${props.stage}`,
});
}
// Produktions-Monitoring und -Alarmierung
this.addProductionMonitoring(props.stage);
// Compliance-Tagging
Tags.of(this).add('DataClassification', 'PII');
Tags.of(this).add('Compliance', 'SOC2-GDPR');
Tags.of(this).add('Service', 'authentication');
Tags.of(this).add('Stage', props.stage);
}
}
Die 400ms-Autorisierungs-Katastrophe#
Unser Legacy-Autorisierungs-Setup tötete die Performance. Jede API-Anfrage benötigte:
- JWT-Dekodierung: 50ms
- Cognito JWK-Abruf: 150ms (kein Caching)
- Signaturverifikation: 80ms
- Datenbankrollen-Lookup: 120ms
- Gesamte Autorisierungszeit: 400ms pro Anfrage
Geschäftsauswirkung: 28% der Gesamtanfrage-Zeit wurde für Autorisierung aufgewendet. Mobile App wurde als "langsam" empfunden. Kundenbeschwerden über API-Reaktionsfähigkeit.
Hochleistungs-JWT-Autorisierung#
Hier ist der caching-optimierte Authorizer, der die Latenz von 400ms auf 12ms reduzierte:
// lib/constructs/auth/high-performance-jwt-authorizer.ts
import {
TokenAuthorizer,
IdentitySource,
IRestApi
} from 'aws-cdk-lib/aws-apigateway';
import { Duration } from 'aws-cdk-lib';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
export class HighPerformanceJwtAuthorizer extends TokenAuthorizer {
constructor(scope: Construct, id: string, props: {
api: IRestApi;
userPoolId: string;
region: string;
stage: string;
}) {
// Optimierte Authorizer-Funktion für Produktion
const authorizerFunction = new NodejsFunction(scope, 'OptimizedAuthorizerFunction', {
entry: 'src/auth/production-jwt-authorizer.ts',
handler: 'handler',
// Provisioned Concurrency für konsistente Performance
reservedConcurrentExecutions: props.stage === 'prod' ? 10 : undefined,
timeout: Duration.seconds(5), // Schneller Timeout für schnelle Fehler
memorySize: 512, // Für JWT-Verarbeitung optimiert
logRetention: RetentionDays.ONE_MONTH,
environment: {
USER_POOL_ID: props.userPoolId,
REGION: props.region,
STAGE: props.stage,
// Performance-Optimierungs-Flags
ENABLE_METRICS: props.stage === 'prod' ? 'true' : 'false',
CACHE_TIMEOUT_MS: '300000', // 5 Minuten
},
bundling: {
// Bundle-Größe für schnellere Cold Starts minimieren
minify: true,
target: 'node20',
// Nur wesentliche Dependencies einschließen
nodeModules: ['jsonwebtoken', 'jwk-to-pem'],
externalModules: ['@aws-sdk/*'],
},
});
super(scope, id, {
restApi: props.api,
handler: authorizerFunction,
identitySource: IdentitySource.header('Authorization'),
// Aggressives Caching für Performance
resultsCacheTtl: Duration.minutes(5),
authorizerName: `${props.api.restApiName}-jwt-authorizer-v2`,
// Strenge Token-Validierung
validationRegex: '^Bearer [A-Za-z0-9\\-_=]+\\.[A-Za-z0-9\\-_=]+\\.[A-Za-z0-9\\-_.+/=]*,
});
}
}
Sicherheits-Migrationsergebnisse#
Nach 3 Wochen intensiver Sicherheits-Neuerstellung sind hier die messbaren Verbesserungen:
Performance-Verbesserungen#
- Autorisierungslatenz: 400ms → 12ms (97% Reduktion)
- Cache-Hit-Rate: 0% → 94% (JWK-Caching)
- API-Antwortzeit: 1,4s → 0,8s Durchschnitt (42% Verbesserung)
- Mobile App wahrgenommene Performance: "Langsam" → "Schnell" Benutzerfeedback
Sicherheitslage#
- Überprivilegierte Funktionen: 47 → 0 (100% Eliminierung)
- Wildcard-IAM-Berechtigungen: 23 Funktionen → 0 Funktionen
- Audit-Trail-Abdeckung: 0% → 100% (alle Auth-Events protokolliert)
- Fehlgeschlagene Auth-Erkennung: Manuell → 30-Sekunden automatisierte Alarme
- Compliance-Status: Fehlgeschlagenes Audit → SOC 2 Type II konform
Betriebseffizienz#
- Auth-Troubleshooting-Zeit: 3 Stunden/Woche → 15 Minuten/Woche
- Sicherheitsvorfälle: 2-3/Monat → 0/Monat (6 Monate laufend)
- Autorisierungs-Cache-Hit-Rate: 94% (5-Minuten-TTL)
- JWT-Validierungsfehler: 15/Tag → 2/Tag (bessere Fehlerbehandlung)
Geschäftsauswirkung#
- Enterprise-Deals freigegeben: $2,3M Verkaufspipeline wiedereröffnet
- Compliance-Audit: SOC 2 Type II bestanden
- GDPR-Strafenrisiko: $180K → $0 (vollständige Compliance)
- Kundenvertrauen: Sichtbare Sicherheitsverbesserungen in Verkaufsdemos
Hart erlernte Sicherheitslektionen#
1. Beginnen Sie immer mit Least Privilege#
Vorher: "Action": "*"
weil "es schneller zu shippen ist"
Nachher: Explizite Berechtigungen für jede Funktion, jede Ressource
Auswirkung: 94% Reduktion der Angriffsfläche
2. Performance und Sicherheit schließen sich nicht gegenseitig aus#
Vorher: "Sicherheit fügt Latenz hinzu" Nachher: Ordnungsgemäßes Caching machte Auth schneller UND sicherer Auswirkung: 97% Latenzreduktion mit stärkerer Sicherheit
3. Audit Trail ist nicht verhandelbar#
Vorher: Null Sichtbarkeit, wer auf was zugriff Nachher: Jede Auth-Entscheidung mit vollem Kontext protokolliert Auswirkung: SOC 2-Audit bestanden, Compliance ermöglicht
4. Cachen Sie alles (sicher)#
Vorher: JWK-Abruf bei jeder Anfrage Nachher: Mehrstufiges Caching mit Invalidierung Auswirkung: 94% Cache-Hit-Rate, unter 20ms Auth
5. Rollenbasierte Zugriffskontrolle skaliert#
Vorher: Ad-hoc-Berechtigungen pro Funktion Nachher: Standardisierte Rollen mit klaren Verantwortlichkeiten Auswirkung: Vereinfachte Verwaltung, bessere Sicherheit
Was kommt als Nächstes#
Ihre Serverless-Anwendung hat jetzt Enterprise-Grade-Authentifizierung und -Autorisierung, die tatsächlich performt. Benutzerverwaltung ist kugelsicher, APIs sind durch optimierte JWT-Verifikation geschützt und IAM-Richtlinien folgen strengen Least-Privilege-Prinzipien.
In Teil 6 werden wir die gesamte Migration zusammenführen:
- Vollständige Migrationsstrategien und Zeitpläne
- Testansätze, die tatsächlich in der Produktion funktionieren
- Rollback-Verfahren, die Ihre Karriere retten
- Performance-Optimierung über den gesamten Stack
- Monitoring und Observability, die Vorfälle verhindern
Die Sicherheitsgrundlage ist solide. Lassen Sie uns diese Migration ordnungsgemäß abschließen.
API Key Management#
Übersetzung folgt.
Migration Security Checklist#
Übersetzung folgt.
Security Migration Results#
Übersetzung folgt.
Hard-Learned Security Lessons#
Übersetzung folgt.
What's Next#
Übersetzung folgt.
Kommentare (0)
An der Unterhaltung teilnehmen
Melde dich an, um deine Gedanken zu teilen und mit der Community zu interagieren
Noch keine Kommentare
Sei der erste, der deine Gedanken zu diesem Beitrag teilt!
Kommentare (0)
An der Unterhaltung teilnehmen
Melde dich an, um deine Gedanken zu teilen und mit der Community zu interagieren
Noch keine Kommentare
Sei der erste, der deine Gedanken zu diesem Beitrag teilt!