İçeriğe atla

Sektöre Göre Kimlik Doğrulama Stratejileri: Bankacılık, Sağlık, E-ticaret, SaaS

Tek tip auth bir efsane: bankacılık, sağlık, e-ticaret ve SaaS kimlik doğrulama mimarisini birbirinden farklı biçimlendirir.

Ayhan Sipahi Ayhan Sipahi

Kimlik doğrulama ve yetkilendirme, iş alanına göre keskin biçimde farklılaşan dört girdinin üzerine oturur: düzenleyici kısıtlamalar, kullanıcı beklentileri, hata biçimleri ve denetim gereksinimleri. Bir bankacılık kimlik doğrulama akışı ile sosyal medya akışı aynı mekanik soruya (“bu istek iddia edilen kullanıcıdan mı geliyor?”) cevap verir, ancak kabul edilebilir cevaplar oturum uzunluğu, çok faktörlü güç, kurtarma yolları ve bir kilitlemenin nasıl göründüğü noktalarında birbirinden ayrılır.

Yeni sistemlerin çoğuna hâlâ uyan varsayılan, yönetilen bir kimlik sağlayıcı üzerinde OIDC ile birlikte OAuth 2.0’dır. Alana göre değişen şey, bunun üstüne eklemek zorunda olduklarınızdır: düzenleyici zemin (PSD2, HIPAA, GDPR, SOC 2), token yaşam döngüsü, MFA ve step-up kuralları, bir de her alanın tolere ettiği hata biçimleri. Bankacılık, sağlık, e-ticaret, kurumsal SSO, IoT ve çok kiracılı SaaS bu dört eksende farklı noktalara oturur; anti-pattern’ler de bir kimlik doğrulama yığını alan sınırını olduğu gibi geçtiğinde ortaya çıkar.

Denetim Altında Bankacılık#

Bireysel bankacılıkta kimlik doğrulama çok farklı kullanıcı profillerini aynı anda taşır: tuşlu telefondan hesabına erişen müşterilerden, bilanço açıklaması sırasında saniyeler içinde emir geçen günlük yatırımcılara kadar. Tek bir giriş akışı hepsini karşılar ve bu akış, bir denetçinin sonradan satır satır önünüze koyacağı uyumluluk çizgisini de tutmak zorundadır.

Denetçiye Gidecek Kanıt#

Bankacılıkta kimlik doğrulama, denetçi karşısında yazılı olarak savunulabilir olmak zorundadır. Bir SOX denetimi JWT implementasyonunun ötesine bakar ve şunları ister:

  • Her kimlik doğrulama girişimi için tam denetim izleri
  • Admin erişiminde görevlerin uygun ayrılması
  • Hassas operasyonlar için donanım güvenlik modülü (HSM) entegrasyonu
  • Hukuki incelemeyi geçen çok faktörlü kimlik doğrulama
// Bankacılık auth kapsamlı audit logging gerektirir
interface BankingAuthEvent {
  userId: string;
  timestamp: Date;
  action: 'login' | 'mfa_challenge' | 'transaction_auth' | 'logout';
  riskScore: number;
  deviceFingerprint: string;
  geoLocation: {
    country: string;
    region: string;
    city: string;
  };
  complianceFlags: {
    pciCompliant: boolean;
    fraudCheckPassed: boolean;
    velocityCheckPassed: boolean;
  };
}

class BankingAuthService {
  async authenticateUser(credentials: UserCredentials): Promise<AuthResult> {
    const authEvent: BankingAuthEvent = {
      userId: credentials.userId,
      timestamp: new Date(),
      action: 'login',
      riskScore: await this.calculateRiskScore(credentials),
      deviceFingerprint: this.getDeviceFingerprint(credentials.request),
      geoLocation: await this.getGeoLocation(credentials.request.ip),
      complianceFlags: {
        pciCompliant: true,
        fraudCheckPassed: false, // Dolandırıcılık kontrolünden sonra güncellenecek
        velocityCheckPassed: false, // Hız kontrolünden sonra güncellenecek
      }
    };

    // Gerçek zamanlı dolandırıcılık tespiti entegrasyonu
    const fraudCheck = await this.fraudDetectionService.checkTransaction(authEvent);
    authEvent.complianceFlags.fraudCheckPassed = fraudCheck.passed;

    // Hız kontrolü (hızlı ardışık girişimleri engelle)
    const velocityCheck = await this.velocityService.checkAttempts(credentials.userId);
    authEvent.complianceFlags.velocityCheckPassed = velocityCheck.passed;

    // Audit izi için her şeyi logla
    await this.auditLogger.logAuthEvent(authEvent);

    if (!fraudCheck.passed || !velocityCheck.passed) {
      throw new AuthenticationError('Kimlik doğrulama güvenlik kontrolleri tarafından engellendi');
    }

    return this.processAuthentication(credentials, authEvent);
  }
}

Tüketici Ölçeğinde Biyometri#

Biyometrik kayıt tüketici tabanının büyük kısmını kapsar, sonra o tabanın sıradan kenarına çarpar: inşaat işçilerinin nasırlı parmak uçları, on iki saatlik vardiyayı eldivenle geçiren hemşireler, bantlı parmaklar, ıslak eller (şaşırtıcı derecede yaygın), parmak izi okuyucusunu şaşırtan çatlak ekranlar. Kademeli bir geri dönüş zinciri bu kullanıcıların kilitlenmesini önler:

class BiometricAuthService {
  async authenticate(userId: string): Promise<AuthResult> {
    try {
      // Birincil: Biyometrik kimlik doğrulama
      return await this.biometricAuth.verify(userId);
    } catch (biometricError) {
      this.logger.warn('Biyometrik auth başarısız, SMS\'e geçiliyor', { 
        userId, 
        error: biometricError.message 
      });
      
      try {
        // Geri dönüş: SMS OTP
        return await this.smsAuth.sendOTP(userId);
      } catch (smsError) {
        this.logger.warn('SMS auth başarısız, telefon aramasına geçiliyor', { 
          userId, 
          error: smsError.message 
        });
        
        // Son geri dönüş: Otomatik telefon araması
        return await this.voiceAuth.makeCall(userId);
      }
    }
  }
}

HIPAA ile Acil Servis Arasında Sıkışan Sağlık Sistemleri#

Sağlıkta kimlik doğrulama aynı anda iki yöne çeker. Hasta verisi güçlü koruma ister; o veriyi okuyan kişi ise çok adımlı hiçbir girişin kaldıramayacağı bir zaman baskısı altında çalışır.

Hemşire acil durumda kayda erişmek zorundadır, HIPAA denetimi ise kaydı kimin, ne zaman ve neden açtığını tam olarak bilmek ister; tasarımın ikisini de aynı akış içinde karşılaması gerekir.

Break-Glass Erişimi#

Sağlık sistemleri “break-glass” erişimine ihtiyaç duyar: normal kimlik doğrulama kurallarının kısa bir süre askıya alındığı acil yollar. Asıl denetim, cam kırıldıktan hemen sonra olanlardır: kimin haberdar edildiği ve erişimin ne kadar hızlı incelendiği.

interface BreakGlassAccess {
  requesterId: string;
  patientId: string;
  emergencyJustification: string;
  witnessId?: string; // Acil durumu doğrulayabilecek başka bir sağlık çalışanı
  autoApprovalCriteria: {
    patientInER: boolean;
    codeBlueActive: boolean;
    surgeryInProgress: boolean;
  };
}

class HealthcareAuthService {
  async requestBreakGlassAccess(request: BreakGlassAccess): Promise<AuthResult> {
    // Otomatik onay kriterlerini karşılayıp karşılamadığını kontrol et
    const autoApprove = Object.values(request.autoApprovalCriteria).some(Boolean);
    
    if (autoApprove) {
      // Anında erişim ver ama inceleme için işaretle
      const access = await this.grantTemporaryAccess(request.requesterId, request.patientId);
      
      // Otomatik inceleme zamanla
      await this.scheduleBreakGlassReview(request);
      
      // Süpervizörü hemen bilgilendir
      await this.notifySupervisor(request);
      
      return access;
    }
    
    // Aksi halde, süpervizör onayı gerekiyor
    return await this.requestSupervisorApproval(request);
  }
  
  private async scheduleBreakGlassReview(request: BreakGlassAccess): Promise<void> {
    // Her break-glass erişimi 24 saat içinde incelenir
    await this.reviewQueue.schedule({
      type: 'break_glass_review',
      requestId: request.requesterId,
      patientId: request.patientId,
      reviewDeadline: new Date(Date.now() + 24 * 60 * 60 * 1000),
      justification: request.emergencyJustification
    });
  }
}

Bağlam Duyarlı Rol Tabanlı Erişim#

Sağlıkta rol hiyerarşileri alışılmadık derecede derindir. Asistan hekim hasta verilerinin çoğunu görürken psikiyatrik notlar kapalı kalır. Uzman doktorda sınır hasta listesinden geçer: kendi hastalarında tam erişim, başka bir uzmanın hastalarında hiçbir şey. Hemşire ise yaşamsal belirtiler ve ilaç verileriyle çalışır; tanısal görüntüleme kapsamının dışında kalır.

Bağlam duyarlı bir izin kontrolü bu durumları kapsar:

interface HealthcareRole {
  roleType: 'resident' | 'attending' | 'nurse' | 'specialist' | 'admin';
  department: string;
  specializations: string[];
  supervisors: string[];
  restrictions: {
    canAccessPsychNotes: boolean;
    canAccessSubstanceAbuseRecords: boolean;
    canAccessMinorRecords: boolean;
    requiresSupervisionFor: string[];
  };
}

interface PatientContext {
  patientId: string;
  currentDepartment: string;
  attendingPhysician: string;
  assignedNurses: string[];
  patientAge: number;
  sensitiveFlags: {
    substanceAbuse: boolean;
    mentalHealth: boolean;
    vip: boolean;
  };
}

class HealthcarePermissionService {
  async canAccessPatientData(
    userId: string, 
    patientContext: PatientContext, 
    dataType: string
  ): Promise<boolean> {
    const userRole = await this.getUserRole(userId);
    
    // Bölüm atamasını kontrol et
    if (userRole.department !== patientContext.currentDepartment && 
        !userRole.specializations.includes('emergency')) {
      return false;
    }
    
    // Hassas veriler için özel işlem
    if (dataType === 'psychiatric_notes' && !userRole.restrictions.canAccessPsychNotes) {
      return false;
    }
    
    if (patientContext.sensitiveFlags.substanceAbuse && 
        !userRole.restrictions.canAccessSubstanceAbuseRecords) {
      return false;
    }
    
    // Reşit olmayan hastalar ek izin gerektirir
    if (patientContext.patientAge < 18 && !userRole.restrictions.canAccessMinorRecords) {
      return false;
    }
    
    return true;
  }
}

E-ticarette Misafir Ödeme Sorunu#

E-ticaret gelirinin büyük bir kısmı hesap açmayacak insanlardan gelir. Yine de davranışlarını takip etmeniz, terk ettikleri sepetleri saklamanız ve ödemelerini güvenle almanız gerekir. Ödeme öncesinde hesap açmayı zorunlu tutarsanız dönüşüm düşer; hesapları tamamen atlarsanız sipariş takibi ve müşteri desteği zorlaşır.

Alışılmış çıkış yolu aşamalı kimlik doğrulamadır. Bilgiyi kademeli toplar; her adım oturum ilerledikçe kendini hak eder:

interface GuestSession {
  sessionId: string;
  fingerprint: string;
  cartItems: CartItem[];
  shippingAddress?: Address;
  paymentMethod?: PaymentMethod;
  emailCollected?: string;
  phoneCollected?: string;
  accountCreationPrompted: boolean;
}

class EcommerceAuthService {
  async handleGuestCheckout(session: GuestSession): Promise<CheckoutResult> {
    // Anonim ödemeyle başla
    let userContext = await this.createGuestContext(session);
    
    // Aşamalı bilgi toplama
    if (!session.emailCollected && session.cartItems.length > 0) {
      // İlk olarak, sadece sipariş onayı için e-posta iste
      userContext = await this.collectEmail(session);
    }
    
    if (session.cartItems.some(item => item.value > 100) && !session.phoneCollected) {
      // Yüksek değerli siparişler için kargo güncellemeleri için telefon topla
      userContext = await this.collectPhone(session);
    }
    
    // Ödemede, faydalarıyla hesap oluşturma teklif et
    if (!session.accountCreationPrompted && 
        await this.hasMultipleOrders(session.fingerprint)) {
      const accountOffer = {
        benefits: [
          'Bir dahaki sefere daha hızlı ödeme',
          'Sipariş geçmişi takibi',
          'Özel üye indirimleri'
        ],
        prefilledData: {
          email: session.emailCollected,
          phone: session.phoneCollected,
          address: session.shippingAddress
        }
      };
      
      return this.offerAccountCreation(userContext, accountOffer);
    }
    
    return this.processGuestCheckout(userContext);
  }
  
  private async hasMultipleOrders(fingerprint: string): Promise<boolean> {
    // Bu cihazın/parmak izinin daha önce sipariş verip vermediğini kontrol et
    const orderHistory = await this.orderService.getOrdersByFingerprint(fingerprint);
    return orderHistory.length > 1;
  }
}

Sorunun ikinci yarısı ödeme adımındadır. Her işlemcinin 3D Secure, dolandırıcılık önleme ve düzenleyici uyumluluk için ayrı gereksinimleri vardır; bu yüzden ödeme akışına ne kadar sürtünme ekleneceği bir skorlama kararına dönüşür:

interface PaymentAuthContext {
  userId?: string;
  sessionId: string;
  paymentAmount: number;
  currency: string;
  shippingAddress: Address;
  billingAddress: Address;
  riskFactors: {
    newDevice: boolean;
    unusualLocation: boolean;
    highValueOrder: boolean;
    velocityFlags: string[];
  };
}

class PaymentAuthService {
  async authenticatePayment(context: PaymentAuthContext): Promise<PaymentAuthResult> {
    const riskScore = await this.calculatePaymentRisk(context);
    
    if (riskScore > 75) {
      // Yüksek risk: Ek kimlik doğrulama gerekiyor
      return this.requireStrongAuth(context);
    }
    
    if (riskScore > 50) {
      // Orta risk: 3D Secure kullan
      return this.require3DSecure(context);
    }
    
    if (context.riskFactors.newDevice && context.paymentAmount > 500) {
      // Yeni cihaz + yüksek değer: SMS onayı gönder
      return this.requireSMSConfirmation(context);
    }
    
    // Düşük risk: Normal olarak işle
    return this.processPayment(context);
  }
  
  private async calculatePaymentRisk(context: PaymentAuthContext): Promise<number> {
    let risk = 0;
    
    if (context.riskFactors.newDevice) risk += 20;
    if (context.riskFactors.unusualLocation) risk += 25;
    if (context.riskFactors.highValueOrder) risk += 30;
    if (context.riskFactors.velocityFlags.length > 0) risk += 15 * context.riskFactors.velocityFlags.length;
    
    // Kullanıcı geçmişine göre ayarlama yap
    if (context.userId) {
      const userHistory = await this.getUserPaymentHistory(context.userId);
      if (userHistory.successfulPayments > 10) risk -= 10; // Güvenilir kullanıcı
      if (userHistory.chargebacks > 0) risk += 20; // Önceki iade talebi
    }
    
    return Math.min(risk, 100);
  }
}

Kurumsal SSO’nun SAML Tarafı#

Kurumsal SSO entegrasyonlarında saatlerin çoğu üç işe gider: sertifika yönetimi, öznitelik haritalama ve neredeyse hiçbir şey söylemeyen SAML hatalarını ayıklamak. Satış cümlesi “müşterinin Active Directory’siyle entegre oluyoruz” olur; iş ise küçük uyumsuzluklardan oluşan bir kuyruktur.

Tipik bir arıza şöyle görünür: servisin verdiği tek hata “Kimlik doğrulama başarısız” olur; gerçek neden ise SAML kütüphanesinin assertion koşullarını doğrularken reddettiği bir zaman damgası formatı ya da saat kaymasıdır. Bu genel hata mesajı, düzeltmenin kendisinden çok daha fazla zaman yakar; bu yüzden doğrulama çağrısından önce ayrıştırılmış assertion koşullarını loglayın.

SAML Öznitelik Haritalama#

Her kurumsal müşterinin kullanıcı özniteliklerini yapılandırma şekli farklı. Bazıları kullanıcı adı olarak e-posta adresi kullanıyor, diğerleri çalışan kimliği. Bazıları bölüm bilgilerini özel özniteliklerde saklıyor, diğerleri grup üyeliklerine gömüyor.

interface SAMLAttributeMapping {
  customerId: string;
  mappings: {
    username: string; // 'email', 'employeeId', 'uid', 'samAccountName' olabilir
    email: string;
    firstName: string;
    lastName: string;
    department?: string;
    roles: string[]; // Grup adları, rol öznitelikleri veya özel talepler olabilir
  };
  transformations: {
    lowercaseUsername: boolean;
    extractDomainFromEmail: boolean;
    mapDepartmentCodes: Record<string, string>;
    rolePrefix?: string; // Bazı müşteriler tüm rollere 'ROLE_' öneki ekler
  };
}

class EnterpriseSSAMLService {
  async processSAMLResponse(
    samlResponse: string, 
    customerId: string
  ): Promise<UserProfile> {
    const mapping = await this.getAttributeMapping(customerId);
    const attributes = this.extractSAMLAttributes(samlResponse);
    
    // Farklı kullanıcı adı formatlarını işle
    let username = attributes[mapping.mappings.username];
    if (mapping.transformations.lowercaseUsername) {
      username = username.toLowerCase();
    }
    if (mapping.transformations.extractDomainFromEmail && username.includes('@')) {
      username = username.split('@')[0];
    }
    
    // Rol/grupları işle
    let roles = this.extractRoles(attributes, mapping.mappings.roles);
    if (mapping.transformations.rolePrefix) {
      roles = roles.map(role => 
        role.startsWith(mapping.transformations.rolePrefix) 
          ? role 
          : mapping.transformations.rolePrefix + role
      );
    }
    
    // Bölüm haritalamayı işle
    let department = attributes[mapping.mappings.department];
    if (department && mapping.transformations.mapDepartmentCodes[department]) {
      department = mapping.transformations.mapDepartmentCodes[department];
    }
    
    return {
      username,
      email: attributes[mapping.mappings.email],
      firstName: attributes[mapping.mappings.firstName],
      lastName: attributes[mapping.mappings.lastName],
      department,
      roles,
      customerId
    };
  }
}

Just-In-Time Provizyon#

Kurumsal müşteriler, kullanıcıların SSO üzerinden ilk kez giriş yaptıklarında otomatik olarak provizyon edilmesini istiyor. Ama aynı zamanda izinleri kontrol etmek, ayrılan çalışanları ele almak ve audit izlerini sürdürmek istiyorlar.

interface JITProvisioningConfig {
  customerId: string;
  autoCreateUsers: boolean;
  autoAssignRoles: boolean;
  defaultRoles: string[];
  roleMapping: Record<string, string[]>; // AD grup -> uygulama rolleri
  disableOnMissingAttributes: string[]; // Bu öznitelikler eksikse kullanıcıyı devre dışı bırak
  notificationRules: {
    notifyOnNewUser: boolean;
    notifyOnRoleChange: boolean;
    notifyOnDisabled: boolean;
    recipients: string[];
  };
}

class JITProvisioningService {
  async provisionUser(
    samlProfile: UserProfile, 
    config: JITProvisioningConfig
  ): Promise<UserAccount> {
    const existingUser = await this.findExistingUser(samlProfile.username, config.customerId);
    
    if (existingUser) {
      return this.updateExistingUser(existingUser, samlProfile, config);
    }
    
    if (!config.autoCreateUsers) {
      throw new Error(`Kullanıcı ${samlProfile.username} bulunamadı ve otomatik oluşturma devre dışı`);
    }
    
    // Yeni kullanıcı oluştur
    const newUser = await this.createUser({
      username: samlProfile.username,
      email: samlProfile.email,
      firstName: samlProfile.firstName,
      lastName: samlProfile.lastName,
      department: samlProfile.department,
      customerId: config.customerId,
      source: 'saml_jit'
    });
    
    // SAML özniteliklerine dayalı roller ata
    const roles = this.mapSAMLRolesToApplication(samlProfile.roles, config.roleMapping);
    await this.assignRoles(newUser.id, [...config.defaultRoles, ...roles]);
    
    // Bildirimleri gönder
    if (config.notificationRules.notifyOnNewUser) {
      await this.notifyUserCreated(newUser, config.notificationRules.recipients);
    }
    
    return newUser;
  }
  
  private async updateExistingUser(
    user: UserAccount, 
    samlProfile: UserProfile, 
    config: JITProvisioningConfig
  ): Promise<UserAccount> {
    // Kullanıcı özniteliklerini güncelle
    const updatedUser = await this.updateUserAttributes(user, {
      email: samlProfile.email,
      firstName: samlProfile.firstName,
      lastName: samlProfile.lastName,
      department: samlProfile.department
    });
    
    // Eksik gerekli öznitelikleri kontrol et
    for (const requiredAttr of config.disableOnMissingAttributes) {
      if (!samlProfile[requiredAttr]) {
        await this.disableUser(user.id, `Gerekli öznitelik eksik: ${requiredAttr}`);
        return updatedUser;
      }
    }
    
    // Konfigürasyon izin veriyorsa rolleri güncelle
    if (config.autoAssignRoles) {
      const newRoles = this.mapSAMLRolesToApplication(samlProfile.roles, config.roleMapping);
      const currentRoles = await this.getUserRoles(user.id);
      
      if (this.rolesChanged(currentRoles, newRoles)) {
        await this.updateUserRoles(user.id, newRoles);
        
        if (config.notificationRules.notifyOnRoleChange) {
          await this.notifyRoleChange(user, currentRoles, newRoles, config.notificationRules.recipients);
        }
      }
    }
    
    return updatedUser;
  }
}

Karşıda Kullanıcı Yokken Cihaz Kimliği#

Yukarıdaki bütün modeller akışın karşı ucunda bir insan varsayar, IoT ise o insanı ortadan kaldırır. Termostatta CAPTCHA’yı yanıtlayacak, duman dedektörüne şifre yazacak kimse yoktur; sertifika yaşam döngüsünün cihaz tarafında hiçbir insan adımı olmadan yürümesi gerekir.

Bir akıllı ev filosunda ucuz sensörler ile pahalı kameralar aynı ağda yaşar. Tasarım emeğinin büyük kısmı, bunlardan biri ele geçirildikten sonrasına gider: iptal, karantina ve şüpheli bir cihazın hâlâ yapabileceği dar operasyon kümesi.

interface DeviceCertificate {
  deviceId: string;
  serialNumber: string;
  manufacturerId: string;
  modelNumber: string;
  certificate: string;
  privateKey: string; // Cihazda güvenli şekilde saklanır
  issueDate: Date;
  expirationDate: Date;
  revoked: boolean;
  revokedReason?: string;
  parentCertificate?: string; // Sertifika zincirleri için
}

class IoTDeviceAuthService {
  async authenticateDevice(
    deviceId: string, 
    certificate: string, 
    signature: string
  ): Promise<DeviceAuthResult> {
    // Sertifikanın iptal edilmediğini doğrula
    let certInfo = await this.getCertificateInfo(certificate);
    if (certInfo.revoked) {
      throw new DeviceAuthError('Sertifika iptal edildi', certInfo.revokedReason);
    }
    
    // Son kullanma tarihini kontrol et
    if (new Date() > certInfo.expirationDate) {
      // Otomatik sertifika yenileme dene
      const renewalResult = await this.attemptCertificateRenewal(deviceId, certInfo);
      if (!renewalResult.success) {
        throw new DeviceAuthError('Sertifika süresi doldu ve yenileme başarısız');
      }
      certInfo = renewalResult.newCertificate;
    }
    
    // İmzayı sertifikayla doğrula
    const isValidSignature = await this.verifySignature(
      signature, 
      deviceId, 
      certInfo.certificate
    );
    
    if (!isValidSignature) {
      throw new DeviceAuthError('Geçersiz cihaz imzası');
    }
    
    // Cihazın karantinada olup olmadığını kontrol et
    const quarantineStatus = await this.getQuarantineStatus(deviceId);
    if (quarantineStatus.quarantined) {
      return {
        authenticated: true,
        quarantined: true,
        allowedOperations: ['status_report', 'security_update'],
        quarantineReason: quarantineStatus.reason
      };
    }
    
    return {
      authenticated: true,
      quarantined: false,
      allowedOperations: this.getDevicePermissions(certInfo.modelNumber),
      certificateExpiration: certInfo.expirationDate
    };
  }
  
  async quarantineDevice(
    deviceId: string, 
    reason: string, 
    reportedBy: string
  ): Promise<void> {
    await this.quarantineService.quarantineDevice({
      deviceId,
      reason,
      reportedBy,
      timestamp: new Date(),
      allowedOperations: ['status_report', 'security_update'], // Sadece minimal operasyonlar
      reviewRequired: true
    });
    
    // Cihaz sahiplerini bilgilendir
    const device = await this.getDevice(deviceId);
    await this.notificationService.notifyDeviceQuarantine(device.ownerId, {
      deviceName: device.name,
      deviceType: device.type,
      reason,
      actions: [
        'Cihazda alışılmadık davranış kontrol et',
        'Cihaz firmware\'ini güncelle',
        'Sorun devam ederse desteğe başvur'
      ]
    });
  }
}

İmzalı Firmware Güncellemeleri#

IoT’deki en endişe verici saldırı vektörlerinden biri kötü niyetli firmware güncellemeleri. Cihaz güvenliği ihlal edilmiş olsa bile sadece meşru firmware’in yüklendiğinden emin olman gerekiyor.

interface FirmwareUpdate {
  deviceModel: string;
  version: string;
  firmwareHash: string;
  signature: string;
  releaseNotes: string;
  criticalityLevel: 'low' | 'medium' | 'high' | 'critical';
  rolloutPercentage: number; // Kademeli dağıtım
  prerequisites: {
    minimumCurrentVersion?: string;
    requiredFeatures: string[];
    incompatibleVersions: string[];
  };
}

class SecureFirmwareService {
  async authenticateFirmwareUpdate(
    deviceId: string,
    updateRequest: FirmwareUpdate
  ): Promise<FirmwareUpdateResult> {
    // Cihazın bu firmware için uygun olduğunu doğrula
    const device = await this.getDevice(deviceId);
    if (device.model !== updateRequest.deviceModel) {
      throw new FirmwareError('Firmware model uyuşmazlığı');
    }
    
    // Önkoşulları kontrol et
    if (updateRequest.prerequisites.minimumCurrentVersion && 
        this.compareVersions(device.currentFirmwareVersion, updateRequest.prerequisites.minimumCurrentVersion) < 0) {
      throw new FirmwareError('Mevcut firmware versiyonu doğrudan güncelleme için çok eski');
    }
    
    if (updateRequest.prerequisites.incompatibleVersions.includes(device.currentFirmwareVersion)) {
      throw new FirmwareError('Mevcut versiyondan doğrudan güncelleme desteklenmiyor');
    }
    
    // Firmware imzasını doğrula
    const isValidSignature = await this.verifyFirmwareSignature(
      updateRequest.firmwareHash,
      updateRequest.signature
    );
    
    if (!isValidSignature) {
      throw new FirmwareError('Geçersiz firmware imzası');
    }
    
    // Dağıtım uygunluğunu kontrol et
    const rolloutEligible = await this.checkRolloutEligibility(
      deviceId, 
      updateRequest.rolloutPercentage
    );
    
    if (!rolloutEligible) {
      return {
        eligible: false,
        reason: 'Cihaz mevcut dağıtım grubunda değil',
        nextCheckTime: this.calculateNextRolloutTime(updateRequest.rolloutPercentage)
      };
    }
    
    // Tüm kontroller geçti - güncellemeyi yetkilendir
    return {
      eligible: true,
      firmwareUrl: await this.generateSecureFirmwareUrl(deviceId, updateRequest),
      updateWindow: this.calculateUpdateWindow(updateRequest.criticalityLevel),
      rollbackEnabled: true
    };
  }
  
  private async checkRolloutEligibility(
    deviceId: string, 
    rolloutPercentage: number
  ): Promise<boolean> {
    // Cihazın dağıtım grubunda olup olmadığını belirlemek için tutarlı hash kullan
    const deviceHash = this.hashDeviceId(deviceId);
    const hashValue = parseInt(deviceHash.substring(0, 8), 16);
    const threshold = (rolloutPercentage / 100) * 0xffffffff;
    
    return hashValue <= threshold;
  }
}

Tek Ürün, Birden Çok Kimlik Sağlayıcı#

Çok kiracılı SaaS’ta her kiracı kendi kimlik sağlayıcısını, özel rollerini ve veri izolasyonu gereksinimlerini getirir. Bir müşteri Azure AD ister, diğeri Okta kullanır, üçüncüsünde 2003’ten kalma özel bir LDAP kurulumu vardır. Bunların hiçbiri uygulama kodunda durmamalıdır; sağlayıcı seçimi, oturum kuralları ve parola politikası kiracı bazlı konfigürasyona taşınır:

interface TenantConfig {
  tenantId: string;
  subdomain: string;
  customDomain?: string;
  identityProviders: {
    primary: IdentityProviderConfig;
    fallback?: IdentityProviderConfig;
    socialLogins: SocialLoginConfig[];
  };
  sessionConfig: {
    timeoutMinutes: number;
    maxConcurrentSessions: number;
    requireMFA: boolean;
    mfaMethods: ('sms' | 'totp' | 'email')[];
  };
  passwordPolicy: {
    minLength: number;
    requireSpecialChars: boolean;
    requireNumbers: boolean;
    requireUppercase: boolean;
    maxAge: number; // gün
    preventReuse: number; // önceki şifreler
  };
}

class MultiTenantAuthService {
  async authenticateUser(
    credentials: UserCredentials,
    tenantContext: TenantContext
  ): Promise<AuthResult> {
    const tenantConfig = await this.getTenantConfig(tenantContext.tenantId);
    
    // Kimlik doğrulamayı uygun provider'a yönlendir
    if (tenantConfig.identityProviders.primary.type === 'saml') {
      return this.authenticateViaSAML(credentials, tenantConfig);
    }
    
    if (tenantConfig.identityProviders.primary.type === 'oidc') {
      return this.authenticateViaOIDC(credentials, tenantConfig);
    }
    
    // Dahili kimlik doğrulamaya geri dön
    const authResult = await this.authenticateInternal(credentials, tenantConfig);
    
    // Kiracıya özel oturum konfigürasyonu uygula
    return this.applyTenantSessionConfig(authResult, tenantConfig);
  }
  
  private async authenticateInternal(
    credentials: UserCredentials,
    config: TenantConfig
  ): Promise<AuthResult> {
    // Şifreyi kiracı politikasına karşı doğrula
    if (!this.validatePasswordPolicy(credentials.password, config.passwordPolicy)) {
      throw new AuthError('Şifre kiracı politika gereksinimlerini karşılamıyor');
    }
    
    const user = await this.validateCredentials(credentials, config.tenantId);
    
    // MFA gereksinimi kontrol et
    if (config.sessionConfig.requireMFA) {
      const mfaResult = await this.initiateMFA(user, config.sessionConfig.mfaMethods);
      if (!mfaResult.completed) {
        return {
          authenticated: false,
          mfaRequired: true,
          mfaChallenge: mfaResult.challenge
        };
      }
    }
    
    // Eşzamanlı oturum limitlerini kontrol et
    await this.enforceSessionLimits(user.id, config.sessionConfig.maxConcurrentSessions);
    
    return {
      authenticated: true,
      user,
      sessionTimeout: config.sessionConfig.timeoutMinutes * 60 * 1000
    };
  }
}

Giriş Yolu Darboğaza Dönüşünce#

Kimlik doğrulama çoğu zaman kullanıcının etkileşime girdiği ilk bileşendir; yavaş veya güvenilmezse kullanıcı uygulamanın geri kalanına hiç ulaşamaz. Yük testleri genellikle uygulama yolunu hedefleyip giriş yolunu atlar; sonuçta auth servisi, sistemin geri kalanının kaldırabildiği trafiğin küçük bir bölümüne göre boyutlanır ve bu açık kendini çoğunlukla lansmanda gösterir. Önbellek, sık okunan yolu veritabanından çeker; yeter ki geçersizleştirme hikâyesi önbellekle aynı anda yazılsın:

interface AuthCacheStrategy {
  userCache: {
    ttl: number; // saniye
    maxSize: number;
    evictionPolicy: 'lru' | 'lfu' | 'ttl';
  };
  sessionCache: {
    ttl: number;
    distributed: boolean; // Çoklu instance dağıtımları için
    compressionEnabled: boolean;
  };
  permissionCache: {
    ttl: number;
    hierarchicalCaching: boolean; // Rol hiyerarşilerini önbelleğe al
    invalidationStrategy: 'immediate' | 'eventual' | 'scheduled';
  };
}

class ScalableAuthService {
  private userCache: LRUCache<string, UserProfile>;
  private sessionCache: RedisCache<string, SessionData>;
  private permissionCache: HierarchicalCache<string, Permission[]>;
  
  constructor(private config: AuthCacheStrategy) {
    this.userCache = new LRUCache({
      max: config.userCache.maxSize,
      ttl: config.userCache.ttl * 1000
    });
    
    this.sessionCache = new RedisCache({
      ttl: config.sessionCache.ttl,
      compression: config.sessionCache.compressionEnabled
    });
    
    this.permissionCache = new HierarchicalCache({
      ttl: config.permissionCache.ttl,
      invalidationStrategy: config.permissionCache.invalidationStrategy
    });
  }
  
  async validateSession(sessionToken: string): Promise<SessionValidationResult> {
    // Önce önbelleği dene
    const cachedSession = await this.sessionCache.get(sessionToken);
    if (cachedSession && !this.isSessionExpired(cachedSession)) {
      return {
        valid: true,
        userId: cachedSession.userId,
        permissions: await this.getCachedPermissions(cachedSession.userId),
        fromCache: true
      };
    }
    
    // Önbellek kaybı - veritabanından doğrula
    const session = await this.validateSessionFromDB(sessionToken);
    if (session.valid) {
      // Gelecekteki istekler için oturumu önbelleğe al
      await this.sessionCache.set(sessionToken, {
        userId: session.userId,
        createdAt: session.createdAt,
        lastActiveAt: new Date(),
        tenantId: session.tenantId
      });
    }
    
    return { ...session, fromCache: false };
  }
  
  async getCachedPermissions(userId: string): Promise<Permission[]> {
    const cached = await this.permissionCache.get(userId);
    if (cached) {
      return cached;
    }
    
    // Veritabanından yükle ve önbelleğe al
    const permissions = await this.loadUserPermissions(userId);
    await this.permissionCache.set(userId, permissions);
    
    return permissions;
  }
  
  // İzin değişikliklerini önbellek geçersizleştirmesi ile işle
  async updateUserPermissions(userId: string, newPermissions: Permission[]): Promise<void> {
    await this.updatePermissionsInDB(userId, newPermissions);
    
    // Önbelleği geçersizleştir
    await this.permissionCache.invalidate(userId);
    
    // Hiyerarşik önbelleğe alma etkinse, bağımlı girişleri geçersizleştir
    if (this.config.permissionCache.hierarchicalCaching) {
      const dependentUsers = await this.findUsersWithInheritedPermissions(userId);
      await Promise.all(
        dependentUsers.map(depUserId => this.permissionCache.invalidate(depUserId))
      );
    }
  }
}

Sık Auth Sorguları için İndeksler#

Kimlik doğrulama sistemleri hedefli veritabanı optimizasyonundan fayda sağlayan belirli sorgu desenleri oluşturur:

-- Kullanıcı adıyla kullanıcı arama optimizasyonu (en yaygın auth sorgusu)
CREATE INDEX CONCURRENTLY idx_users_username_active 
ON users (username) 
WHERE active = true;

-- Oturum doğrulama sorgularını optimize et
CREATE INDEX CONCURRENTLY idx_sessions_token_expires 
ON user_sessions (session_token, expires_at) 
WHERE expires_at > NOW();

-- Rol hiyerarşisi ile izin sorgularını optimize et
CREATE INDEX CONCURRENTLY idx_user_roles_user_id 
ON user_roles (user_id) 
INCLUDE (role_id, granted_at, expires_at);

-- Uyumluluk raporlama için audit sorgularını optimize et
CREATE INDEX CONCURRENTLY idx_auth_events_user_timestamp 
ON auth_events (user_id, timestamp DESC) 
WHERE event_type IN ('login', 'logout', 'mfa_challenge', 'permission_change');

Alandan Alana Taşınanlar#

En net taşınabilir ders sıralamayla ilgili. Önce teknolojiyi seçip sonra onu uyumlu hale getirmeye çalışmak pahalı yeniden düzenlemeyle sonuçlanır; uyumluluk gereksinimlerinden (sağlıkta HIPAA, finansta PCI-DSS ve SOX) başlayıp geriye doğru çalışmak daha ucuzdur, çünkü teknik kararları eninde sonunda uyumluluk belirler. Yeni bir projede sorular, veto gücü azalan sırayla şöyle dizilir:

  1. Uyumluluk gereksinimleri neler? (HIPAA, PCI-DSS, GDPR, SOX, vb.)
  2. Beklenen kullanıcı ölçeği nedir? (Binlerce ile milyonlarca arasında fark var)
  3. Kullanıcı deneyimi beklentisi nedir? (Tüketici uygulaması mı, kurumsal araç mı)
  4. Tehdit modeli nedir? (Script kiddie’ler mi, ulus devlet aktörleri mi)
  5. Bütçe kısıtı nedir? (İnşa et, satın al veya hibrit)
  6. Ekibin uzmanlığı nedir? (Bakamayacağınız şeyi inşa etmeyin)

Hata durumları da aynı erken ilgiyi hak eder ve inatla alana özgüdür. Sosyal medya platformu kullanıcıyı ara sıra kilitlemeyi göze alabilir; bankacılık uygulaması alamaz, sağlık sistemi acil erişim yoluna ihtiyaç duyar, IoT cihazının ise hiç geri dönüş yöntemi olmayabilir. Kilitlenme, kurtarma ve acil erişim davranışı tam da hiçbir kütüphanenin hazır vermediği parçalardır; bu yüzden kütüphane seçiminden önce kararlaştırılmalıdır.

Kullanılabilirlik bir güvenlik özelliğidir. Kâğıt üzerinde sağlam ama pratikte acı veren kimlik doğrulama baypas edilir; doğan geçici çözümler de güvenlik modelini baltalar. Aynı mantık gözlemlenebilirlik için de geçerli: kimlik doğrulama sistemleri ince şekillerde bozulur; başarısız girişlerdeki küçük bir artış, şifre sıfırlama isteklerindeki ani yükseliş veya alışılmadık bir coğrafi desen, çoğu zaman credential stuffing’in ya da hesap ele geçirmenin ilk görünür işaretidir.

Daha sessiz bir nokta: kullanıcılar er ya da geç yeni bir kimlik doğrulama sistemine taşınmak zorunda kalır; tetikleyici güvenlik iyileştirmesi, uyumluluk değişikliği veya iş gereksinimi olabilir. Kullanıcı veritabanınızı ve kimlik doğrulama akışlarınızı kademeli taşımayı destekleyecek şekilde tasarlayın.

Varsayılanın Sınırı#

OIDC ile OAuth 2.0 varsayılanını yalnızca bir alan kısıtı onu yetersiz bıraktığında terk edin: bankacılıkta HSM zorunluluğu veya işlem tutarına bağlı step-up kuralı, sağlıkta zorunlu geriye dönük incelemeli break-glass erişimi, IoT’de yönlendirilecek etkileşimli bir kullanıcı olmadığı için sertifika tabanlı cihaz kimliği. Bu kısıtların her biri varsayılanın üstüne bir katman ekler; yerine başka bir şey aramadan önce kendi alanınızı bu listeye karşı kontrol edin.

Kaynaklar#

İlgili yazılar