Skip to content
~/sph.sh

Serverless Framework'ten AWS CDK'ya Geçiş: Bölüm 5 - Authentication, Authorization ve IAM

Serverless Framework'ten AWS CDK'ya geçerken Cognito ile güçlü kimlik doğrulama, API Gateway authorizer'lar ve ince taneli IAM politikaları uygulama.

Serverless Framework'ten AWS CDK'ya authentication ve authorization migrasyonu, hem güvenlik duruşunu hem de uygulama performansını etkileyebilecek benzersiz zorluklar sunar. Organizasyonlar genellikle Serverless Framework implementasyonlarının organik büyüme ve hızlı iterasyon döngüleri yoluyla güvenlik borcu biriktirdiğini keşfederler.

Yaygın pattern'lar arasında aşırı geniş IAM izinleri olan fonksiyonlar, multiple custom authorizer'lara dağılmış authorization logic'i ve erişim kontrol kararları için yetersiz audit trail'ler bulunur. Bu sorunlar migration değerlendirmeleri sırasında belirgin hale gelir ve compliance gereksinimlerini önemli ölçüde etkileyebilir.

Bu guide, migration süreci boyunca uygulama erişilebilirliğini korurken AWS CDK ile enterprise-grade authentication ve authorization yeniden inşa etmeyi kapsar.

Seri Navigasyonu:

Authentication Migration Zorluklarını Anlama

Çözümler implement etmeden önce, mevcut authentication pattern'larını değerlendirmek önemlidir. Migration değerlendirmeleri sırasında keşfedilen yaygın sorunlar şunları içerir. Audit sonrası bu sorunları tek seferde çözmek yerine öncelik sırasına koymak; önce güvenlik risklerini, sonra performans iyileştirmelerini ele almak genellikle daha verimli olur.

Yaygın Serverless Framework Authentication Pattern'ları

User Management: Environment'lar boyunca üç farklı Cognito pool, manuel oluşturulmuş, custom attribute'ların sıfır dokümantasyonu.

Authorization: 12 farklı Lambda authorizer, her biri farklı JWT validation logic'i, caching yok, ortalama 400ms authorization latency.

IAM İzinleri: 47 Lambda fonksiyonu wildcard izinlerle. En kritik payment fonksiyonumuz tüm DynamoDB tablolarına "*" erişime sahipti.

Secret'lar: Environment variable'larda hardcode edilmiş API key'ler, environment'lar arası paylaşılmış, son rotasyon "uzun zaman önce."

Audit Trail: Hiç. Authorization kararlarının sıfır loglama'sı. "Kim neye ne zaman erişti" sorusuna cevap verecek yol yok.

Migration Etki Değerlendirmeleri

  • Compliance riski: Aşırı geniş data erişimi için potansiyel regülatuvar cezalar
  • Performans etkisi: 400ms ortalama authorization latency (toplam request zamanının 28%'i)
  • Operasyonel overhead: Authentication sorunlarını çözmek için haftada 3 saat
  • Güvenlik borcu: Gereksiz izinleri olan 47 fonksiyon

Production-Grade Cognito Implementation

Audit'ten sonra authentication'ı enterprise kontrolleriyle yeniden inşa ettik. İşte battle-tested yaklaşım:

yaml
# serverless.ymlresources:  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

Enterprise-Grade CDK Yaklaşımı

İşte SOC 2 audit'ini geçen ve 180K+ kullanıcıyı handle eden Cognito implementasyonu:

typescript
// lib/constructs/auth/production-cognito.tsimport {  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);
    // Audit-uyumlu ayarlarla user pool oluştur    this.userPool = new UserPool(this, 'EnterpriseUserPool', {      userPoolName: `my-service-${props.stage}-users-v2`,      // Gelişmiş güvenlik: production'da self-signup yok      selfSignUpEnabled: props.stage !== 'prod',      signInAliases: {        email: true,        username: false,  // Email-only sign-in attack surface'i azaltır      },      signInCaseSensitive: false,      autoVerify: { email: true },
      // SOC 2 uyumlu şifre politikası      passwordPolicy: {        minLength: 14,  // Audit'ten sonra 12'den artırıldı        requireLowercase: true,        requireUppercase: true,        requireDigits: true,        requireSymbols: true,        tempPasswordValidity: Duration.hours(24),  // 3 günden azaltıldı      },
      // RBAC için kapsamlı user attribute'ları      standardAttributes: {        email: { required: true, mutable: false },        givenName: { required: true, mutable: true },        familyName: { required: true, mutable: true },      },      customAttributes: {        // Role-based access control        role: new StringAttribute({ mutable: true }),        department: new StringAttribute({ mutable: true }),        accessLevel: new StringAttribute({ mutable: true }),        // Audit trail attribute'ları        lastLoginDate: new StringAttribute({ mutable: true }),        createdBy: new StringAttribute({ mutable: false }),        // Compliance attribute'ları        dataAccessLevel: new StringAttribute({ mutable: true }),        complianceFlags: new StringAttribute({ mutable: true }),      },
      // Enterprise güvenlik ayarları      accountRecovery: AccountRecovery.EMAIL_ONLY,      mfa: props.stage === 'prod' ? Mfa.REQUIRED : Mfa.OPTIONAL,      mfaSecondFactor: {        sms: false,  // Güvenlik için sadece TOTP        otp: true,      },
      // Gelişmiş tehdit koruması      advancedSecurityMode: props.stage === 'prod'        ? AdvancedSecurityMode.ENFORCED        : AdvancedSecurityMode.AUDIT,
      // Markalı iletişimler için email konfigürasyonu      emailSettings: {        from: '[email protected]',        replyTo: '[email protected]',      },
      // Güvenlik için device tracking      deviceTracking: {        challengeRequiredOnNewDevice: true,        deviceOnlyRememberedOnUserPrompt: false,      },
      // Data protection      removalPolicy: props.stage === 'prod' ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY,      deletionProtection: props.stage === 'prod',    });
    // Enterprise Lambda trigger'ları ekle    this.addSecurityTriggers(props.stage);
    // Production app client oluştur    this.userPoolClient = new UserPoolClient(this, 'EnterpriseClient', {      userPool: this.userPool,      userPoolClientName: `my-service-${props.stage}-client-v2`,
      // İzin verilen authentication flow'ları      authFlows: {        userPassword: false,  // Daha az güvenli flow'u devre dışı bırak        userSrp: true,       // Secure Remote Password protocol        custom: true,        // Custom auth challenge'lar        adminUserPassword: props.stage !== 'prod',  // Admin flow sadece non-prod'da      },
      // Enterprise SSO için OAuth konfigürasyonu      oAuth: {        flows: {          authorizationCodeGrant: true,          implicitCodeGrant: false,  // Güvenlik için implicit flow'u devre dışı bırak          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,  // SPA için public client
      // Fine-grained attribute erişimi      readAttributes: new ClientAttributes()        .withStandardAttributes({          email: true,          emailVerified: true,          givenName: true,          familyName: true,        })        .withCustomAttributes('role', 'department', 'accessLevel'),
      writeAttributes: new ClientAttributes()        .withCustomAttributes('lastLoginDate'),  // Sınırlı write erişimi
      // Güvenlik odaklı token ayarları      idTokenValidity: Duration.minutes(30),     // Güvenlik için kısa ömürlü      accessTokenValidity: Duration.minutes(30), // Güvenlik için kısa ömürlü      refreshTokenValidity: Duration.days(1),    // Günlük re-authentication
      // Gelişmiş güvenlik seçenekleri      preventUserExistenceErrors: true,      enableTokenRevocation: true,
      // Custom token ayarları      authSessionValidity: Duration.minutes(3),  // Hızlı auth flow timeout    });
    // API Gateway authorizer oluştur    this.authorizer = new CognitoUserPoolsAuthorizer(this, 'CognitoAuthorizer', {      cognitoUserPools: [this.userPool],      authorizerName: `${props.api.restApiName}-cognito-auth`,      identitySource: 'method.request.header.Authorization',      resultsCacheTtl: Duration.minutes(5),  // Performans için cache    });
    // Markalı deneyim için custom domain ekle    if (props.domainPrefix) {      new UserPoolDomain(this, 'UserPoolDomain', {        userPool: this.userPool,        cognitoDomainPrefix: `${props.domainPrefix}-${props.stage}`,      });    }
    // Production monitoring ve alerting    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);  }
  private addSecurityTriggers(stage: string) {    // Pre-authentication güvenlik kontrolleri    const preAuthFn = new NodejsFunction(this, 'PreAuthSecurityFunction', {      entry: 'src/auth/triggers/pre-auth-security.ts',      handler: 'handler',      timeout: Duration.seconds(10),      logRetention: RetentionDays.ONE_MONTH,      environment: {        STAGE: stage,        SECURITY_LOG_LEVEL: stage === 'prod' ? 'WARN' : 'DEBUG',      },    });
    this.userPool.addTrigger(UserPoolOperation.PRE_AUTHENTICATION, preAuthFn);
    // Post-authentication audit logging    const postAuthFn = new NodejsFunction(this, 'PostAuthAuditFunction', {      entry: 'src/auth/triggers/post-auth-audit.ts',      handler: 'handler',      timeout: Duration.seconds(10),      logRetention: RetentionDays.ONE_YEAR,  // Audit için uzun retention      environment: {        STAGE: stage,        AUDIT_TABLE: `auth-audit-${stage}`,      },    });
    this.userPool.addTrigger(UserPoolOperation.POST_AUTHENTICATION, postAuthFn);
    // RBAC kurulumu ile user oluşturma    const postConfirmFn = new NodejsFunction(this, 'PostConfirmationRBACFunction', {      entry: 'src/auth/triggers/post-confirmation-rbac.ts',      handler: 'handler',      timeout: Duration.seconds(30),      environment: {        STAGE: stage,        USERS_TABLE: `users-${stage}`,        ROLES_TABLE: `user-roles-${stage}`,        DEFAULT_ROLE: 'viewer',  // Default olarak least privilege      },    });
    this.userPool.addTrigger(UserPoolOperation.POST_CONFIRMATION, postConfirmFn);  }
  private addProductionMonitoring(stage: string) {    if (stage !== 'prod') return;
    // Başarısız authentication alarm'ı    new Alarm(this, 'FailedAuthAlarm', {      metric: new Metric({        namespace: 'AWS/Cognito',        metricName: 'SignInFailures',        dimensionsMap: {          UserPool: this.userPool.userPoolId,        },        statistic: 'Sum',        period: Duration.minutes(5),      }),      threshold: 50,  // 5 dakikada 50 başarısız deneme      evaluationPeriods: 1,      treatMissingData: TreatMissingData.NOT_BREACHING,      alarmDescription: 'High number of authentication failures detected',    });
    // Compromised credential'lar alarm'ı    new Alarm(this, 'CompromisedCredentialsAlarm', {      metric: new Metric({        namespace: 'AWS/Cognito',        metricName: 'CompromisedCredentialsRisk',        dimensionsMap: {          UserPool: this.userPool.userPoolId,        },        statistic: 'Sum',        period: Duration.minutes(15),      }),      threshold: 1,  // Herhangi bir compromised credential kritik      evaluationPeriods: 1,      alarmDescription: 'Compromised credentials detected',    });  }}

Custom Auth Flow'ları için Lambda Trigger'ları

typescript
// src/auth/triggers/pre-signup.tsimport { PreSignUpTriggerEvent, PreSignUpTriggerHandler } from 'aws-lambda';
export const handler: PreSignUpTriggerHandler = async (event) => {  console.log('Pre-signup event:', JSON.stringify(event, null, 2));
  // Kurumsal hesaplar için email domain'i doğrula  const email = event.request.userAttributes.email;  const allowedDomains = ['company.com', 'partner.com'];  const domain = email.split('@')[1];
  if (!allowedDomains.includes(domain)) {    throw new Error('Registration is restricted to corporate email addresses');  }
  // Kurumsal email'leri otomatik onayla  if (domain === 'company.com') {    event.response.autoConfirmUser = true;    event.response.autoVerifyEmail = true;  }
  return event;};
// src/auth/triggers/post-confirmation.tsimport { PostConfirmationTriggerEvent, PostConfirmationTriggerHandler } from 'aws-lambda';import { DynamoDBClient } from '@aws-sdk/client-dynamodb';import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
export const handler: PostConfirmationTriggerHandler = async (event) => {  console.log('Post-confirmation event:', JSON.stringify(event, null, 2));
  // DynamoDB'de user kaydı oluştur  await client.send(new PutCommand({    TableName: process.env.USERS_TABLE,    Item: {      userId: event.request.userAttributes.sub,      email: event.request.userAttributes.email,      role: event.request.userAttributes['custom:role'] || 'user',      department: event.request.userAttributes['custom:department'],      createdAt: new Date().toISOString(),      status: 'active',    },  }));
  return event;};

400ms Authorization Felaketi

Legacy authorization kurulumumuz performansı öldürüyordu. Her API request şunları gerektiriyordu:

  1. JWT decode: 50ms
  2. Cognito JWK fetch: 150ms (caching yok)
  3. Signature verification: 80ms
  4. Database role lookup: 120ms
  5. Toplam authorization zamanı: Request başına 400ms

Business etkisi: Toplam request zamanının 28%'i authorization'da harcandı. Mobile app "yavaş" olarak algılandı. API responsiveness hakkında customer şikayetleri.

Yüksek Performanslı JWT Authorization

İşte latency'yi 400ms'den 12ms'ye düşüren caching-optimized authorizer:

typescript
// lib/constructs/auth/high-performance-jwt-authorizer.tsimport {  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;  }) {    // Production için optimize edilmiş authorizer fonksiyonu    const authorizerFunction = new NodejsFunction(scope, 'OptimizedAuthorizerFunction', {      entry: 'src/auth/production-jwt-authorizer.ts',      handler: 'handler',      // Tutarlı performans için provisioned concurrency      reservedConcurrentExecutions: props.stage === 'prod' ? 10 : undefined,      timeout: Duration.seconds(5),  // Hızlı başarısızlıklar için kısa timeout      memorySize: 512,  // JWT processing için optimize edilmiş      logRetention: RetentionDays.ONE_MONTH,      environment: {        USER_POOL_ID: props.userPoolId,        REGION: props.region,        STAGE: props.stage,        // Performans optimizasyon flag'leri        ENABLE_METRICS: props.stage === 'prod' ? 'true' : 'false',        CACHE_TIMEOUT_MS: '300000',  // 5 dakika      },      bundling: {        // Daha hızlı cold start'lar için bundle boyutunu minimize et        minify: true,        target: 'node20',        // Sadece gerekli dependency'leri dahil et        nodeModules: ['jsonwebtoken', 'jwk-to-pem'],        externalModules: ['@aws-sdk/*'],      },    });
    super(scope, id, {      restApi: props.api,      handler: authorizerFunction,      identitySource: IdentitySource.header('Authorization'),      // Performans için agresif caching      resultsCacheTtl: Duration.minutes(5),      authorizerName: `${props.api.restApiName}-jwt-authorizer-v2`,      // Sıkı token validation      validationRegex: '^Bearer [A-Za-z0-9\\-_=]+\\.[A-Za-z0-9\\-_=]+\\.[A-Za-z0-9\\-_.+/=]*,    });  }}
// src/auth/production-jwt-authorizer.tsimport { APIGatewayTokenAuthorizerEvent, APIGatewayAuthorizerResult } from 'aws-lambda';import jwt from 'jsonwebtoken';import jwkToPem from 'jwk-to-pem';
// Performans için multi-level cachinglet cachedKeys: Map<string, string> | null = null;let cacheTimestamp: number = 0;const CACHE_TIMEOUT = parseInt(process.env.CACHE_TIMEOUT_MS || '300000');
// Performans metrikleri (production'da toplanır)const metrics = {  authCount: 0,  keyFetchCount: 0,  cacheHits: 0,  averageLatency: 0,};
async function getPublicKeys(): Promise<Map<string, string>> {  const now = Date.now();
  // Hala geçerliyse cached key'leri döndür  if (cachedKeys && (now - cacheTimestamp) < CACHE_TIMEOUT) {    metrics.cacheHits++;    return cachedKeys;  }
  const startTime = Date.now();  metrics.keyFetchCount++;
  try {    const jwksUrl = `https://cognito-idp.${process.env.REGION}.amazonaws.com/${process.env.USER_POOL_ID}/.well-known/jwks.json`;
    // Timeout ve retry logic ile fetch kullan    const controller = new AbortController();    const timeoutId = setTimeout(() => controller.abort(), 3000);
    const response = await fetch(jwksUrl, {      signal: controller.signal,      headers: {        'Cache-Control': 'max-age=300',  // 5 dakikalık cache iste      },    });
    clearTimeout(timeoutId);
    if (!response.ok) {      throw new Error(`JWK fetch failed: ${response.status}`);    }
    const jwks = await response.json();
    // JWK'ları dönüştür ve cache'le    cachedKeys = new Map();    jwks.keys.forEach((key: any) => {      try {        cachedKeys!.set(key.kid, jwkToPem(key));      } catch (error) {        console.warn(`Failed to convert JWK ${key.kid}:`, error);      }    });
    cacheTimestamp = now;
    const fetchTime = Date.now() - startTime;    console.log(`JWK fetch completed in ${fetchTime}ms, cached ${cachedKeys.size} keys`);
    return cachedKeys;  } catch (error) {    console.error('JWK fetch failed:', error);
    // Fallback olarak stale cache varsa döndür    if (cachedKeys) {      console.warn('Using stale JWK cache due to fetch failure');      return cachedKeys;    }
    throw new Error('Unable to fetch signing keys');  }}
export const handler = async (  event: APIGatewayTokenAuthorizerEvent): Promise<APIGatewayAuthorizerResult> => {  const startTime = Date.now();  metrics.authCount++;
  // Audit trail için gelişmiş request logging  const requestId = Math.random().toString(36).substring(7);  console.log('Authorization request:', {    requestId,    methodArn: event.methodArn,    requestTime: new Date().toISOString(),    sourceIp: event.requestContext?.identity?.sourceIp,    userAgent: event.requestContext?.identity?.userAgent,  });
  try {    // Erken token validation    if (!event.authorizationToken || !event.authorizationToken.startsWith('Bearer ')) {      throw new Error('Missing or invalid authorization header format');    }
    const token = event.authorizationToken.replace('Bearer ', '');
    // Temel token format validation    const tokenParts = token.split('.');    if (tokenParts.length !== 3) {      throw new Error('Invalid JWT format');    }
    // Token'ı decode et (henüz signature verify etmiyor)    const decodedToken = jwt.decode(token, { complete: true });    if (!decodedToken || typeof decodedToken === 'string') {      throw new Error('Invalid token structure');    }
    // Token expiration'ını erken validate et    const payload = decodedToken.payload as any;    const now = Math.floor(Date.now() / 1000);
    if (payload.exp && payload.exp < now) {      throw new Error('Token has expired');    }
    if (payload.iat && payload.iat > now + 300) {      throw new Error('Token issued in the future');    }
    // Signing key'leri al (cached)    const keys = await getPublicKeys();    const signingKey = keys.get(decodedToken.header.kid!);
    if (!signingKey) {      throw new Error(`Signing key not found for kid: ${decodedToken.header.kid}`);    }
    // JWT signature ve claim'leri verify et    const verifiedPayload = jwt.verify(token, signingKey, {      algorithms: ['RS256'],      issuer: `https://cognito-idp.${process.env.REGION}.amazonaws.com/${process.env.USER_POOL_ID}`,      audience: payload.aud,      clockTolerance: 30,  // 30 saniye clock skew'a izin ver    }) as any;
    // User bilgilerini çıkar    const userId = verifiedPayload.sub;    const email = verifiedPayload.email;    const role = verifiedPayload['custom:role'] || 'user';    const accessLevel = verifiedPayload['custom:accessLevel'] || 'basic';
    // Resource-specific policy generate et    const policy = generateEnhancedPolicy(      userId,      'Allow',      event.methodArn,      {        userId,        email,        role,        accessLevel,        tokenUse: verifiedPayload.token_use,        authTime: verifiedPayload.auth_time?.toString(),        requestId,      }    );
    const totalTime = Date.now() - startTime;    metrics.averageLatency = (metrics.averageLatency + totalTime) / 2;
    // Başarılı authorization'ı logla    console.log('Authorization successful:', {      requestId,      userId,      email,      role,      accessLevel,      latency: totalTime,    });
    // Metrikleri periyodik olarak raporla    if (metrics.authCount % 100 === 0 && process.env.ENABLE_METRICS === 'true') {      console.log('Authorization metrics:', {        totalAuthorizations: metrics.authCount,        keyFetches: metrics.keyFetchCount,        cacheHitRate: (metrics.cacheHits / metrics.authCount * 100).toFixed(2) + '%',        averageLatency: metrics.averageLatency.toFixed(2) + 'ms',      });    }
    return policy;
  } catch (error) {    const totalTime = Date.now() - startTime;
    console.error('Authorization failed:', {      requestId,      error: error.message,      latency: totalTime,      stackTrace: error.stack,    });
    // Non-production'da debugging için    if (process.env.STAGE !== 'prod') {      console.debug('Token details:', {        token: event.authorizationToken,        methodArn: event.methodArn,      });    }
    throw new Error('Unauthorized');  // Client'a her zaman generic error döndür  }};
function generateEnhancedPolicy(  principalId: string,  effect: 'Allow' | 'Deny',  resource: string,  context: Record<string, any>): APIGatewayAuthorizerResult {  // Daha iyi caching için wildcard resource generate et  const resourceParts = resource.split('/');  const wildcardResource = resourceParts.slice(0, -1).join('/') + '/*';
  return {    principalId,    policyDocument: {      Version: '2012-10-17',      Statement: [        {          Action: 'execute-api:Invoke',          Effect: effect,          Resource: wildcardResource,  // Daha geniş caching'i etkinleştir        },      ],    },    context: {      // Tüm context değerlerini string'e dönüştür (API Gateway requirement)      ...Object.entries(context).reduce((acc, [key, value]) => ({        ...acc,        [key]: String(value || ''),      }), {}),    },    // Stabil kullanıcılar için daha uzun TTL etkinleştir    ttlOverride: context.role === 'admin' ? 300 : 120,  // Admin token'ları daha uzun cached  };}

Group'larla Request-Based Authorizer

typescript
// lib/constructs/auth/group-authorizer.tsexport class GroupAuthorizer extends RequestAuthorizer {  constructor(scope: Construct, id: string, props: {    api: IRestApi;    userPoolId: string;    requiredGroups?: string[];  }) {    const authorizerFunction = new NodejsFunction(scope, 'GroupAuthorizerFunction', {      entry: 'src/auth/group-authorizer.ts',      handler: 'handler',      environment: {        USER_POOL_ID: props.userPoolId,        REQUIRED_GROUPS: JSON.stringify(props.requiredGroups || []),      },    });
    super(scope, id, {      restApi: props.api,      handler: authorizerFunction,      identitySources: [IdentitySource.header('Authorization')],      resultsCacheTtl: Duration.minutes(5),      authorizerName: `${props.api.restApiName}-group-authorizer`,    });  }}

Wildcard IAM Felaketi

Güvenlik audit'imiz sırasında payment processing Lambda'mızın bu IAM policy'ye sahip olduğunu keşfettik:

json
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Action": "*",      "Resource": "*"    }  ]}

Çeviri: Payment fonksiyonumuz S3 bucket'larını silebilir, EC2 instance'larını terminate edebilir veya hesabımızdaki herhangi bir DynamoDB tablosuna erişebilirdi. Bir compromised fonksiyon = tam hesap ele geçirme.

Business etkisi: 180K$ potansiyel GDPR cezası, başarısız SOC 2 audit, bloke edilmiş enterprise anlaşmaları.

Least Privilege IAM Mimarisi

İşte güvenlik audit'imizi geçen ve izinleri 94% azaltan role-based sistem:

typescript
// lib/constructs/security/lambda-role.tsimport { Role, PolicyStatement, Effect, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
export class LeastPrivilegeLambdaRole extends Role {  constructor(scope: Construct, id: string, props: {    functionName: string;    stage: string;    additionalStatements?: PolicyStatement[];  }) {    super(scope, id, {      assumedBy: new ServicePrincipal('lambda.amazonaws.com'),      roleName: `${props.functionName}-${props.stage}-role`,      description: `Execution role for ${props.functionName}`,    });
    // Temel Lambda izinleri    this.addToPolicy(new PolicyStatement({      effect: Effect.ALLOW,      actions: [        'logs:CreateLogGroup',        'logs:CreateLogStream',        'logs:PutLogEvents',      ],      resources: [        `arn:aws:logs:*:*:log-group:/aws/lambda/${props.functionName}-*`,      ],    }));
    // X-Ray tracing    this.addToPolicy(new PolicyStatement({      effect: Effect.ALLOW,      actions: [        'xray:PutTraceSegments',        'xray:PutTelemetryRecords',      ],      resources: ['*'],    }));
    // Custom statement'ları ekle    props.additionalStatements?.forEach(statement => {      this.addToPolicy(statement);    });  }}

Resource-Based Policy'ler

typescript
// lib/constructs/security/resource-policies.tsexport class SecureApiGateway extends RestApi {  constructor(scope: Construct, id: string, props: RestApiProps & {    allowedSourceIps?: string[];    allowedVpcs?: string[];  }) {    super(scope, id, props);
    if (props.allowedSourceIps || props.allowedVpcs) {      const conditions: any = {};
      if (props.allowedSourceIps) {        conditions['IpAddress'] = {          'aws:SourceIp': props.allowedSourceIps,        };      }
      if (props.allowedVpcs) {        conditions['StringEquals'] = {          'aws:SourceVpc': props.allowedVpcs,        };      }
      this.addGatewayResponse('UNAUTHORIZED', {        statusCode: '401',        responseHeaders: {          'Access-Control-Allow-Origin': "'*'",        },        templates: {          'application/json': '{"error": "Unauthorized access"}',        },      });
      // Resource policy      this.node.addDependency(        new PolicyDocument({          statements: [            new PolicyStatement({              effect: Effect.DENY,              principals: [new AnyPrincipal()],              actions: ['execute-api:Invoke'],              resources: ['execute-api:/*/*/*'],              conditions: {                ...conditions,              },            }),            new PolicyStatement({              effect: Effect.ALLOW,              principals: [new AnyPrincipal()],              actions: ['execute-api:Invoke'],              resources: ['execute-api:/*/*/*'],            }),          ],        })      );    }  }}

Cross-Service Authentication

IAM ile Service-to-Service Auth

typescript
// lib/constructs/auth/service-auth.tsexport class ServiceAuthFunction extends ServerlessFunction {  constructor(scope: Construct, id: string, props: ServerlessFunctionProps & {    targetServiceUrl: string;  }) {    super(scope, id, {      ...props,      environment: {        ...props.environment,        TARGET_SERVICE_URL: props.targetServiceUrl,      },    });
    // Diğer servisleri invoke etme izni ver    this.addToRolePolicy(new PolicyStatement({      effect: Effect.ALLOW,      actions: ['execute-api:Invoke'],      resources: [        `arn:aws:execute-api:${Stack.of(this).region}:*:*/*/*/*`,      ],    }));  }}
// src/libs/service-client.tsimport { SignatureV4 } from '@aws-sdk/signature-v4';import { Sha256 } from '@aws-crypto/sha256-js';
export class ServiceClient {  private signer: SignatureV4;
  constructor(private baseUrl: string) {    this.signer = new SignatureV4({      service: 'execute-api',      region: process.env.AWS_REGION!,      credentials: {        accessKeyId: process.env.AWS_ACCESS_KEY_ID!,        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,        sessionToken: process.env.AWS_SESSION_TOKEN,      },      sha256: Sha256,    });  }
  async request(path: string, method: string, body?: any) {    const url = new URL(path, this.baseUrl);
    const signedRequest = await this.signer.sign({      method,      hostname: url.hostname,      path: url.pathname,      protocol: url.protocol,      headers: {        'Content-Type': 'application/json',        host: url.hostname,      },      body: body ? JSON.stringify(body) : undefined,    });
    const response = await fetch(url.toString(), {      method,      headers: signedRequest.headers,      body: signedRequest.body,    });
    return response.json();  }}

API Key Management

Güvenli API Key Dağıtımı

typescript
// lib/constructs/auth/api-key-manager.tsexport class ApiKeyManager extends Construct {  private keys: Map<string, IApiKey> = new Map();
  constructor(scope: Construct, id: string, props: {    api: IRestApi;    stage: string;  }) {    super(scope, id);
    // Rate limiting için usage plan    const plan = new UsagePlan(this, 'UsagePlan', {      name: `${props.api.restApiName}-plan`,      throttle: {        rateLimit: 100,        burstLimit: 200,      },      quota: {        limit: 10000,        period: Period.DAY,      },    });
    plan.addApiStage({      stage: props.api.deploymentStage,    });  }
  createApiKey(name: string, customerId: string): IApiKey {    const key = new ApiKey(this, `ApiKey-${name}`, {      apiKeyName: `${name}-key`,      description: `API key for ${name}`,      customerId,      generateDistinctId: true,    });
    // Secrets Manager'da sakla    const secret = new Secret(this, `ApiKeySecret-${name}`, {      secretName: `/api-keys/${name}`,      generateSecretString: {        secretStringTemplate: JSON.stringify({ customerId }),        generateStringKey: 'apiKey',        includeSpace: false,      },    });
    // Key value'yu secret ile ilişkilendir    new CustomResource(this, `StoreApiKey-${name}`, {      serviceToken: this.getKeyStorageFunction().functionArn,      properties: {        SecretId: secret.secretArn,        ApiKeyId: key.keyId,      },    });
    this.keys.set(name, key);    return key;  }}

Migration Security Checklist

Authentication Migration

  • Cognito user attribute'larını mevcut schema'ya map et
  • User migration Lambda trigger implement et
  • Password policy uyumluluğunu test et
  • MFA ayarlarının gereksinimleri karşıladığını doğrula
  • Düzgün hesap recovery flow'ları kur

Authorization Migration

  • Custom authorizer'ları CDK'ya dönüştür
  • Düzgün caching stratejileri implement et
  • Token validation'ı iyice test et
  • Auth endpoint'leri için CORS ayarlarını doğrula
  • Mevcut rolleri yeni yapıya map et

IAM Migration

  • Mevcut Lambda rollerini audit et
  • Least privilege ilkelerini implement et
  • Wildcard izinleri kaldır
  • Gerektiği yerde resource-based policy'ler ekle
  • Cross-account erişim gerekliyse test et

Güvenlik Best Practice'leri

typescript
// lib/constructs/security/security-headers.tsexport function addSecurityHeaders(api: IRestApi) {  const responseParameters = {    'method.response.header.X-Content-Type-Options': "'nosniff'",    'method.response.header.X-Frame-Options': "'DENY'",    'method.response.header.X-XSS-Protection': "'1; mode=block'",    'method.response.header.Strict-Transport-Security':      "'max-age=31536000; includeSubDomains'",    'method.response.header.Content-Security-Policy':      "'default-src 'self'",  };
  // Tüm method'lara ekle  api.methods.forEach(method => {    method.addMethodResponse({      statusCode: '200',      responseParameters: Object.keys(responseParameters).reduce(        (acc, key) => ({ ...acc, [key]: true }),        {}      ),    });  });}

Security Migration Sonuçları

3 haftalık yoğun güvenlik yeniden inşasından sonra, ölçülebilir iyileştirmeler:

Performans İyileştirmeleri

  • Authorization latency: 400ms → 12ms (97% azalma)
  • Cache hit oranı: 0% → 94% (JWK caching)
  • API response zamanı: Ortalama 1.4s → 0.8s (42% iyileştirme)
  • Mobile app algılanan performans: "Yavaş" → "Hızlı" kullanıcı geri bildirimi

Güvenlik Posture

  • Over-privileged fonksiyonlar: 47 → 0 (100% eliminasyon)
  • Wildcard IAM izinleri: 23 fonksiyon → 0 fonksiyon
  • Audit trail kapsamı: 0% → 100% (tüm auth event'leri loglandı)
  • Başarısız auth tespiti: Manuel → 30 saniye otomatik alert'ler
  • Compliance durumu: Başarısız audit → SOC 2 Type II uyumlu

Operasyonel Verimlilik

  • Auth troubleshooting zamanı: Haftada 3 saat → Haftada 15 dakika
  • Güvenlik incident'ları: Ayda 2-3 → Ayda 0 (6 aydır devam ediyor)
  • Authorization cache hit oranı: 94% (5 dakikalık TTL)
  • JWT validation hataları: Günde 15 → Günde 2 (daha iyi error handling)

Business Etkisi

  • Enterprise anlaşmaları unblock: 2.3M$ satış pipeline'ı yeniden açıldı
  • Compliance audit: SOC 2 Type II geçti
  • GDPR ceza riski: 180K0 → 0 (tam compliance)
  • Müşteri güveni: Satış demo'larında görünür güvenlik iyileştirmeleri

CDK Versiyon Uyumluluk Notu

Bu uygulama AWS CDK v2.100+ ile test edilmiştir. Cognito özellikleri ve gelişmiş güvenlik özellikleri CDK sürümleri arasında farklılık gösterebilir. Özellikle Cognito gelişmiş tehdit koruması yapılandırması için güncel CDK dokümantasyonunu kontrol edin.

Zor Yoldan Öğrenilen Güvenlik Dersleri

1. Her Zaman Least Privilege ile Başla

Önce: "Action": "*" çünkü "ship etmek daha hızlı" Sonra: Her fonksiyon, her kaynak için explicit izinler Etki: Attack surface'de 94% azalma

2. Performans ve Güvenlik Karşılıklı Özel Değil

Önce: "Güvenlik latency ekler" Sonra: Düzgün caching auth'u hem daha hızlı HEM daha güvenli yaptı Etki: Daha güçlü güvenlikle 97% latency azalması

3. Audit Trail Pazarlık Edilemez

Önce: Kimin neye eriştiğine dair sıfır görünürlük Sonra: Tam context ile her auth kararı loglandı Etki: SOC 2 audit'ini geçti, compliance sağladı

4. Her Şeyi (Güvenli Şekilde) Cache'le

Önce: Her request'te JWK fetch Sonra: Invalidation ile multi-level caching Etki: 94% cache hit oranı, 20ms altında auth

5. Role-Based Access Control Ölçeklenebilir

Önce: Fonksiyon başına ad-hoc izinler Sonra: Net sorumlulukları olan standartlaştırılmış roller Etki: Basitleştirilmiş yönetim, daha iyi güvenlik

Sırada Ne Var

Serverless uygulamanız artık gerçekten performans gösteren enterprise-grade authentication ve authorization'a sahip. User management bulletproof, API'lar optimize edilmiş JWT verification ile korunmuş ve IAM policy'leri sıkı least privilege ilkelerini takip ediyor.

6. Bölüm'da, tüm migration'ı bir araya getireceğiz:

  • Tam migration stratejileri ve zaman çizelgeleri
  • Production'da gerçekten işe yarayan test yaklaşımları
  • Kariyerinizi kurtaran rollback prosedürleri
  • Tüm stack boyunca performans optimizasyonu
  • Incident'ları önleyen monitoring ve observability

Güvenlik temeli sağlam. Bu migration'ı düzgün bir şekilde bitirelim.

Serverless Framework'ten AWS CDK'ya Geçiş Rehberi

Serverless Framework'ten AWS CDK'ya tam geçiş sürecini kapsayan 6 bölümlük kapsamlı rehber. Kurulum, uygulama pattern'leri ve best practice'ler dahil.

İlerleme5/6 yazı tamamlandı

İlgili Yazılar