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.
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:
- Uyumluluk gereksinimleri neler? (HIPAA, PCI-DSS, GDPR, SOX, vb.)
- Beklenen kullanıcı ölçeği nedir? (Binlerce ile milyonlarca arasında fark var)
- Kullanıcı deneyimi beklentisi nedir? (Tüketici uygulaması mı, kurumsal araç mı)
- Tehdit modeli nedir? (Script kiddie’ler mi, ulus devlet aktörleri mi)
- Bütçe kısıtı nedir? (İnşa et, satın al veya hibrit)
- 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#
- RFC 6749 - OAuth 2.0 Yetkilendirme Çerçevesi (yeni sekmede açılır) - OAuth 2.0 izin türlerini, rolleri ve protokol akışlarını tanımlayan IETF belirtimi
- OpenID Connect Core 1.0 (yeni sekmede açılır) - OAuth 2.0 üzerine kurulu kimlik katmanını tanımlayan resmi OIDC belirtimi
- OWASP Kimlik Doğrulama Kopya Kağıdı (yeni sekmede açılır) - Çok faktörlü kimlik doğrulama ve parola politikaları dahil güvenlik en iyi uygulamaları
- OWASP Yetkilendirme Kopya Kağıdı (yeni sekmede açılır) - Yetkilendirme denetimleri ve erişim yönetimi kalıpları uygulamak için kılavuz
- NIST SP 800-63B Dijital Kimlik Kılavuzları (yeni sekmede açılır) - ABD hükümeti kimlik doğrulama güvence seviyeleri ve kimlik doğrulayıcı gereksinimleri
- OWASP OAuth2 Kopya Kağıdı (yeni sekmede açılır) - Uygulamalarda OAuth 2.0 uygularken dikkate alınacak güvenlik hususları
İlgili yazılar
Ekiplerin tartıştığı authentication, token, erişim kontrolü ve Zero Trust terimleri için tanımlar, implementasyon bağlamı ve varsayılanlar.
security · authentication · oauth2 +2
AWS Cognito ve Verified Permissions ile SaaS yetkilendirmeyi Cedar politikaları, çok kiracılı desenler, JWT akışı ve maliyet analiziyle TypeScript'te kurun.
authorization · aws · authentication +4
AWS Verified Permissions, SpiceDB, OpenFGA, Cerbos ve OPA gibi harici yetkilendirme platformlarını mimari, maliyet ve karar çerçevesi açısından tarafsızca inceliyoruz.
authorization · security · architecture +4
Cedar, Rego, OpenFGA DSL ve Cerbos YAML/CEL politika dillerini söz dizimi, performans, biçimsel doğrulama, araçlar ve TypeScript entegrasyonu açısından karşılaştırıyoruz.
authorization · security · architecture +2
SpiceDB ve Auth0 FGA (OpenFGA) karşılaştırması: şema, tutarlılık modelleri, dağıtım ve ölçeklenebilirlik açısından farklı tercihler yapan iki Zanzibar tabanlı sistem.
authorization · security · architecture +3