İçeriğe atla

Güvenlik Sözlüğü: Her Geliştirici Ekibinin Bilmesi Gereken 50+ Terim

Ekiplerin tartıştığı authentication, token, erişim kontrolü ve Zero Trust terimleri için tanımlar, implementasyon bağlamı ve varsayılanlar.

Ayhan Sipahi Ayhan Sipahi

Güvenlik terimlerindeki karışıklık mimari hataya dönüşür. Login’i yalnızca OAuth2 üzerine kuran bir ekip, haftalar sonra OAuth2’nin yetki verdiğini ama kimlik hakkında hiçbir şey söylemediğini fark eder. Çözüm OIDC’dir; o noktada token akışı hem client hem API tarafında yanlış kurulmuştur.

Aşağıdaki terimler authentication, token, erişim kontrolü ve tasarım incelemelerinde geçen Zero Trust sözlüğünü kapsıyor. Her madde tanımı, işe yarayıp yaramayacağını belirleyen implementasyon detayını ve genelde zaman kaybettiren yanlış anlamayı veriyor. Makul bir varsayılan varsa adı konuyor: yeni sistemlerde OIDC, birincil faktör olarak passkey, rotasyonlu refresh token ile kısa ömürlü access token.

Authentication Temelleri#

MFA (Multi-Factor Authentication)#

MFA, iki veya daha fazla bağımsız faktör tipi gerektiren authentication demektir: bildiğin (şifre), sahip olduğun (telefon) ya da olduğun (parmak izi) bir şey. Gerçek MFA farklı faktör tipleri ister; şifre artı güvenlik sorusu MFA sayılmaz, çünkü ikisi de bilgi faktörüdür.

// FIDO2/WebAuthn ile doğru MFA implementation'ı
interface MFAChallenge {
  primaryAuth: 'password' | 'biometric';
  secondaryAuth: 'totp' | 'webauthn' | 'sms'; // SMS'ten kaçın
  fallbackOptions: string[];
}

const authenticateUser = async (challenge: MFAChallenge) => {
  const primaryResult = await validatePrimary(challenge.primaryAuth);
  if (!primaryResult.success) return { success: false };

  const secondaryResult = await validateSecondary(challenge.secondaryAuth);
  return { success: secondaryResult.success };
};

SMS OTP, SIM swapping ve operatör seviyesinde araya girme saldırılarına açık. NIST SP 800-63B bu nedenle SMS’i kısıtlı (restricted) authenticator kabul ediyor.

2FA vs 2SV (Two-Factor vs Two-Step Verification)#

2FA farklı faktör tiplerini kullanır; 2SV aynı tipi iki kez kullanabilir, karışıklık da tam burada başlıyor.

  • 2FA: Şifre (bilgi) + hardware key (sahiplik)
  • 2SV: Şifre (bilgi) + SMS kodu (yine bilgi - telefon numaranı biliyorsun)

Google’ın kendi UI’ı da bu çizgiyi bulanıklaştırıyor: 2SV implementation’ına “2FA” diyor. Seçim şansın varsa hardware key’ler veya biometric authenticator’larla gerçek 2FA’yı tercih et.

OTP (One-Time Password)#

TOTP (time-based OTP) RFC 6238 standardına göre 30 saniyelik pencereler kullanır. HOTP (counter-based OTP) daha az yaygındır ve RFC 4226’yı izler. SMS OTP, SIM swapping açıkları yüzünden deprecated.

import { authenticator } from 'otplib';

// Yeni kullanıcı için TOTP secret oluştur
const secret = authenticator.generateSecret();

// Kullanıcının TOTP'sini validate et
const validateTOTP = (token: string, secret: string): boolean => {
  try {
    return authenticator.verify({ token, secret });
  } catch (error) {
    return false;
  }
};

// Clock drift için her zaman window tolerance kullan
authenticator.options = { window: 1 }; // 1 step tolerance izin ver

Yeni sistemler için Google Authenticator veya Authy gibi authenticator app’leri kullan; SMS asla.

Biometric Authentication#

Biometric authentication parmak izi, yüz tanıma, iris tarama ve ses paternini kapsar; False Acceptance Rate (FAR) ve False Rejection Rate (FRR) ile değerlendirilir.

Biometric’i asla tek authentication olarak kullanma; her zaman ikinci faktör olarak kullan, çünkü biometric data tehlikeye girerse değiştirilemez.

WebAuthn platform authenticator çağrısı şöyle görünür:

// WebAuthn biometric authentication
const authenticateWithBiometric = async () => {
  const credential = await navigator.credentials.create({
    publicKey: {
      challenge: new Uint8Array(32),
      rp: { name: "Uygulamanız" },
      user: { id: userId, name: username, displayName: displayName },
      pubKeyCredParams: [{ alg: -7, type: "public-key" }],
      authenticatorSelection: {
        authenticatorAttachment: "platform", // Built-in biometric
        userVerification: "required"
      }
    }
  });
};

Biometric template’ler güvenli saklanmalı ve revoke edilebilir kalmalı. Apple’ın Secure Enclave’i ve Android’in TEE’si bunun için mevcut best practice’ler.

Modern Authentication Protokolleri#

OAuth2#

OAuth2 bir authorization framework’üdür. Login’i doğrudan üzerine kuran ekipler, “kullanıcı kim?” sorusuyla karşılaşır karşılaşmaz tıkanır; çünkü access token yalnızca scope taşır.

Grant Type’ları:

  • Authorization Code + PKCE (SPA’lar için önerilen)
  • Client Credentials (service-to-service)
  • Device Flow (IoT/TV app’leri)
  • Implicit Flow (deprecated, güvenlik sorunları)
// PKCE ile güvenli OAuth2 implementation'ı
const oauth2Flow = async () => {
  const codeVerifier = generateCodeVerifier();
  const codeChallenge = await generateCodeChallenge(codeVerifier);

  const authUrl = `${authServer}/authorize?` +
    `response_type=code&` +
    `client_id=${clientId}&` +
    `redirect_uri=${redirectUri}&` +
    `scope=${scope}&` +
    `code_challenge=${codeChallenge}&` +
    `code_challenge_method=S256`;

  // Authorization code ile callback'ten sonra
  const tokenResponse = await exchangeCodeForToken(code, codeVerifier);
};

OIDC (OpenID Connect)#

OIDC, OAuth2’nin üzerine inşa edilmiş bir authentication katmanıdır; ana eklediği şey, kullanıcının kim olduğunu gerçekten söyleyen ID token’dır.

OAuth2’nin üzerine üç parça ekler:

  • ID Token (kullanıcı bilgisi ile JWT)
  • UserInfo Endpoint
  • Discovery Endpoint (/.well-known/openid-configuration)
interface OIDCTokenResponse {
  access_token: string;  // API call'ları için (OAuth2)
  id_token: string;  // Authentication için (OIDC)
  refresh_token: string;  // Yeni token'lar almak için
  token_type: 'Bearer';
  expires_in: number;
}

const validateIdToken = async (idToken: string) => {
  const jwks = await fetchJWKS(issuer);
  const payload = jwt.verify(idToken, jwks);

  // Gerekli claim'leri validate et
  assert(payload.iss === expectedIssuer);
  assert(payload.aud === clientId);
  assert(payload.exp > Date.now() / 1000);

  return payload; // Kullanıcı kimlik bilgisi içerir
};

Modern web app’leri, mobile app’ler ve SPA’lar, kullanıcının kim olduğunu bilmesi gereken her yerde buna ihtiyaç duyar.

ID Token (OIDC)#

ID token, OpenID Connect’in parçası olan ve kullanıcı kimlik bilgilerini içeren bir JWT’dir. Authentication için vardır (kullanıcının kim olduğu); ne yapabileceğini gösteren access token’dan farklıdır.

interface IDTokenPayload {
  iss: string;  // Issuer (kimlik sağlayıcısı)
  sub: string;  // Subject (kullanıcı tanımlayıcısı)
  aud: string;  // Audience (client ID'niz)
  exp: number;  // Expiry timestamp
  iat: number;  // Issued at timestamp
  auth_time: number; // Kullanıcının gerçekte kimlik doğrulaması yaptığı zaman
  nonce?: string;  // Replay attack'leri önler

  // Standart profil claim'leri
  name?: string;
  email?: string;
  picture?: string;

  // Custom claim'ler
  roles?: string[];
  department?: string;
}

const validateIDToken = async (idToken: string) => {
  // 1. Provider'ın public key'lerini kullanarak signature'ı doğrula
  const jwks = await fetchJWKS(issuerUrl + '/.well-known/jwks.json');
  const decoded = jwt.verify(idToken, jwks) as IDTokenPayload;

  // 2. Standart claim'leri doğrula
  if (decoded.iss !== expectedIssuer) throw new Error('Invalid issuer');
  if (decoded.aud !== clientId) throw new Error('Invalid audience');
  if (decoded.exp <= Math.floor(Date.now() / 1000)) throw new Error('Token expired');

  // 3. max_age belirtildiyse auth_time'ı doğrula
  if (maxAge && decoded.auth_time < (Date.now()/1000 - maxAge)) {
    throw new Error('Authentication too old');
  }

  return decoded;
};

İki token birkaç somut noktada ayrışır:

  • ID token’lar client app’in kullanıcının kim olduğunu bilmesi içindir
  • Access token’lar API çağrılarının kullanıcının ne yapabileceğini bilmesi içindir
  • ID token’lar API’lara gönderilmemelidir (access token kullanın)
  • ID token’lar kullanıcı profil bilgilerini içerir

Code review’da tekrar tekrar çıkan hatalar şunlar:

  • ID token’ı API authentication için kullanmak (güvenlik riski)
  • Signature’ı server-side doğrulamamak
  • ID token payload’ında hassas veri saklamak (şifrelenmemiştir)
  • ID token’ları uygulamalar arasında paylaşmak

SAML 2.0#

SAML 2.0, XML tabanlı bir authentication ve authorization standardıdır. Yerleşik identity provider’ı olan büyük kurumlarda hâlâ yaygın ve sırf modernleşmek için OIDC’ye geçmek genelde karşılığını vermiyor: enterprise müşteriler hâlihazırda işlettikleri identity provider entegrasyonlarına göre standartlaşır, bu yüzden protokol seçimi onların altyapısını takip eder.

İki flow tipi var:

  • SP-initiated (uygulamanız flow’u başlatır)
  • IdP-initiated (identity provider flow’u başlatır)

Implementation karmaşıklığı yüksek: XML signature’lar, sertifika yönetimi ve hızla karmaşıklaşan konfigürasyon.

<!-- SAML Assertion Örneği -->
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
  <saml:Subject>
    <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
      user@company.com
    </saml:NameID>
  </saml:Subject>
  <saml:AttributeStatement>
    <saml:Attribute Name="Role">
      <saml:AttributeValue>Manager</saml:AttributeValue>
    </saml:Attribute>
  </saml:AttributeStatement>
</saml:Assertion>

Protocol Karşılaştırma Matrisi#

ProtocolFormatKarmaşıklıkEnterprise BenimsenmeMobile Destek
OAuth2JSONDüşükOrtaMükemmel
OIDCJSONOrtaBüyüyorMükemmel
SAMLXMLYüksekDominantZayıf

Token Güvenliği#

JWT (JSON Web Token)#

Yapı: Header.Payload.Signature (hepsi Base64URL encoded)

JWT’yi zayıf nokta olmaktan çıkaran birkaç pratik var:

  • Access token’lar için maksimum 15 dakika expiry (30 günlük bir token, tek bir sızıntıyı bir aylık erişime çevirir)
  • Mümkün olduğunda RS256’yı (asymmetric) HS256’ya (symmetric) tercih et
  • Payload’a hassas veri koyma: Base64 herkesin çözebileceği, geri döndürülebilir bir encoding’dir
interface JWTPayload {
  iss: string;  // Issuer
  sub: string;  // Subject (kullanıcı ID)
  aud: string;  // Audience
  exp: number;  // Expiry (UNIX timestamp)
  iat: number;  // Issued at
  nbf: number;  // Not before
  jti: string;  // JWT ID (revocation için)
}

const validateJWT = (token: string, publicKey: string): JWTPayload => {
  const decoded = jwt.verify(token, publicKey, {
    algorithms: ['RS256'],
    issuer: expectedIssuer,
    audience: expectedAudience
  });

  // Expiry'yi her zaman server-side kontrol et
  if (decoded.exp <= Math.floor(Date.now() / 1000)) {
    throw new Error('Token expired');
  }

  return decoded as JWTPayload;
};

JWKS (JSON Web Key Set)#

JWKS, JWT imzalarını doğrulamak için kullanılan public key’leri içeren, genellikle /.well-known/jwks.json’da servis edilen bir JSON dokümanıdır. OAuth2 ve OpenID Connect provider’ları public key’lerini token doğrulaması için bu şekilde yayınlar.

interface JWKSKey {
  kty: string;  // Key type (RSA, EC)
  use: string;  // Usage (sig for signature)
  kid: string;  // Key ID
  n: string;  // RSA modulus (Base64URL)
  e: string;  // RSA exponent (Base64URL)
}

const fetchJWKS = async (issuerUrl: string): Promise<JWKSKey[]> => {
  const response = await fetch(`${issuerUrl}/.well-known/jwks.json`);
  const jwks = await response.json();
  return jwks.keys;
};

// kid (Key ID) ile doğru key'i bul
const getSigningKey = (jwks: JWKSKey[], kid: string): JWKSKey => {
  const key = jwks.find(k => k.kid === kid);
  if (!key) throw new Error(`Key ${kid} not found in JWKS`);
  return key;
};

Key rotation’ın doğrulamayı sessizce kırmasını önleyen birkaç kural var:

  • JWKS’i cache et ama otomatik refresh implement et (hiç yenilenmeyen bir cache, provider key rotation yaptıktan sonra her imza kontrolünü düşürür)
  • kid (Key ID) match’ini her zaman verify et; kid tanınmıyorsa set’i yeniden çek
  • Key rotation’ı graceful handle et

Signing Key / Private Key#

Signing key, JWT’leri dijital olarak imzalamak için kullanılan ve authenticity’yi kanıtlayan signature kısmını oluşturan kriptografik private key’dir. Güvenli saklanmalı, düzenli rotate edilmeli ve erişimi yalnızca token issuer’la sınırlı kalmalı.

// Server-side JWT signing (RS256)
const signJWT = (payload: JWTPayload, privateKey: string): string => {
  return jwt.sign(payload, privateKey, {
    algorithm: 'RS256',
    keyid: currentKeyId,  // JWKS lookup için kid include et
    expiresIn: '15m'
  });
};

// Key rotation stratejisi
interface SigningKeyPair {
  kid: string;
  privateKey: string;
  publicKey: string;
  createdAt: Date;
}

const rotateSigningKeys = async (): Promise<void> => {
  // Yeni keypair generate et
  const newKeyPair = generateRSAKeyPair();

  // Yeni private key'i güvenli şekilde store et
  await keyStore.store(newKeyPair.kid, newKeyPair.privateKey);

  // JWKS'i yeni public key ile update et
  await updateJWKS(newKeyPair.publicKey, newKeyPair.kid);

  // Eski key'i grace period boyunca aktif tut
  scheduleKeyRetirement(previousKeyId, '24h');
};

Private key’in kendisini en kritik varlık olarak ele al:

  • Private key’leri HSM veya secure key management system’de store et
  • Key’leri düzenli rotate et (minimum aylık)
  • Private key’leri asla log’larda veya client code’da expose etme
  • Güçlü key length’leri kullan (RSA 2048+ veya EC P-256+)

Access Token’lar vs Refresh Token’lar#

Access token’lar kısa ömürlüdür (5-15 dakika) ve API call’larını kapsar; refresh token’lar daha uzun yaşar (7 gün) ve yeni access token almak için var.

İkisini farklı şekilde sakla:

  • Access token’lar: Sadece memory’de (localStorage asla)
  • Refresh token’lar: Mümkün olduğunda güvenli, httpOnly cookie’ler
class TokenManager {
  private accessToken: string | null = null;
  private refreshToken: string | null = null;
  private refreshPromise: Promise<string> | null = null;

  async getValidAccessToken(): Promise<string> {
    if (this.accessToken && !this.isTokenExpired(this.accessToken)) {
      return this.accessToken;
    }

    // Concurrent refresh request'leri önle
    if (!this.refreshPromise) {
      this.refreshPromise = this.refreshAccessToken();
    }

    return this.refreshPromise;
  }

  private async refreshAccessToken(): Promise<string> {
    try {
      const response = await fetch('/auth/refresh', {
        method: 'POST',
        credentials: 'include', // httpOnly refresh token'ı dahil et
      });

      const { access_token, refresh_token } = await response.json();

      this.accessToken = access_token;
      // Refresh token rotation - her zaman yeni refresh token al
      if (refresh_token) {
        this.refreshToken = refresh_token;
      }

      return access_token;
    } finally {
      this.refreshPromise = null;
    }
  }
}

Refresh token’ları her kullanımda rotate et, böylece tek bir compromise süresiz geçerli kalmaz.

Bearer Token’lar vs API Key’ler#

Bearer token’lar dinamik ve kısa ömürlüdür, OAuth2/JWT ecosystem’inin parçasıdır; API key’ler statik, uzun ömürlü ve daha basit bir authentication mekanizmasıdır.

Seçim genelde çağıranı takip eder:

  • Bearer token’lar: Kullanıcı authentication’ı, dinamik permissions
  • API key’ler: Service-to-service, basit entegrasyonlar, webhook’lar
// Bearer Token (kullanıcı auth için tercih edilen)
const callAPIWithBearer = async (endpoint: string) => {
  const token = await tokenManager.getValidAccessToken();
  return fetch(endpoint, {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });
};

// API Key (service auth için kabul edilebilir)
const callAPIWithKey = async (endpoint: string) => {
  return fetch(endpoint, {
    headers: {
      'X-API-Key': process.env.API_KEY, // Asla client'ta expose etme
      'User-Agent': 'MyService/1.0'
    }
  });
};

Zero Trust & Modern Güvenlik#

Zero Trust Architecture#

Zero Trust tek bir ilke üzerinde çalışır: asla güvenme, her zaman doğrula. Identity, device, network, uygulama ve data doğrulamasını tek sürekli kontrol olarak kapsar.

Ekipler genelde dört aşamadan geçer:

  1. Identify: Tüm kullanıcıları, cihazları, uygulamaları, dataları map’le
  2. Protect: Least-privilege erişim kontrollerini implement et
  3. Detect: Anomali ve tehditler için monitör et
  4. Respond: Otomatik tehdit response ve remediation

Ekipler arasında yürüyen bir programdır: identity, cihaz durumu ve segmentasyon farklı ekiplerin işidir, bu dört aşamanın sıralaması da takvimi belirler.

// Zero Trust policy örneği
interface ZeroTrustPolicy {
  user: {
    verified: boolean;
    riskScore: number;
    mfaCompleted: boolean;
  };
  device: {
    managed: boolean;
    compliant: boolean;
    lastSeen: Date;
  };
  network: {
    location: string;
    trustLevel: 'high' | 'medium' | 'low';
  };
  resource: {
    classification: 'public' | 'internal' | 'confidential' | 'restricted';
    requiredClearance: number;
  };
}

const evaluateAccess = (policy: ZeroTrustPolicy): boolean => {
  // Continuous verification - her request
  if (!policy.user.verified || !policy.user.mfaCompleted) return false;
  if (!policy.device.managed || !policy.device.compliant) return false;
  if (policy.user.riskScore > policy.resource.requiredClearance) return false;

  return true;
};

ZTNA (Zero Trust Network Access)#

ZTNA, geleneksel VPN’i microsegmentation ile değiştirir; client bağlandığı anda tüm ağa erişim yerine uygulama bazında erişim verir.

İki türü var:

  • Agent-based: Her cihazda software
  • Agentless: Browser tabanlı erişim

Zscaler, Palo Alto Prisma ve Cloudflare Access bu alanda öne çıkan vendor’lar.

SASE (Secure Access Service Edge)#

SASE, SD-WAN, ZTNA, CASB, FWaaS ve SWG’yi networking ve güvenliği birleştiren tek bir cloud-native platformda toplar. Tek satıcıda toplamak bileşenler arası entegrasyon işini ortadan kaldırır. Satıcı riskini de tek bir kontrol düzleminde yoğunlaştırır.

Web Güvenlik Header’ları#

HSTS (HTTP Strict Transport Security)#

HSTS, belirtilen süre için HTTPS’i zorlar; Strict-Transport-Security: max-age=31536000; includeSubDomains; preload gibi bir header kullanır.

Kısa bir max-age ile başlayıp uzun süreli politikaya kademeli geç:

# Test için kısa süre ile başla
add_header Strict-Transport-Security "max-age=300" always;

# Test'ten sonra süreyi artır
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

# Son olarak maksimum güvenlik için preload ekle
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Chrome, Firefox ve Edge de HSTS domain’lerinin hardcoded preload listelerini tutuyor.

CSP (Content Security Policy)#

CSP, bir sayfanın yükleyebileceği kaynakları kontrol ederek XSS ve injection saldırılarını önler.

Kademeli bir rollout, production’ı ilk günden bozmaktan korur:

# Report-only mode ile başla
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

# Temel policy
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'

# Sıkı policy (hedef)
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:

Yaygın pitfall, third-party script’lerin bozulması; Google Analytics veya chat widget’ları gibi. Her zaman önce report-only mode’da test et.

Certificate Pinning (Deprecated)#

HPKP deprecated ve büyük tarayıcılardan kaldırıldı, header artık hiçbir şeyi korumuyor; ayrıca key’ler beklenmedik şekilde değişirse hâlâ self-DOS riski taşıyor.

Certificate Transparency (CT) ve CAA record’ların birleşimi onun yerini aldı:

; CAA record örneği
example.com. CAA 0 issue "letsencrypt.org"
example.com. CAA 0 issuewild ";"
example.com. CAA 0 iodef "mailto:security@example.com"

Erişim Kontrol Modelleri#

RBAC (Role-Based Access Control)#

RBAC, kullanıcıları rollere, rolleri de izinlere bağlar ve en iyi stabil organizasyon yapılarında işler.

-- RBAC Database Schema
CREATE TABLE roles (
  id UUID PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  description TEXT
);

CREATE TABLE permissions (
  id UUID PRIMARY KEY,
  resource VARCHAR(255) NOT NULL,
  action VARCHAR(255) NOT NULL
);

CREATE TABLE role_permissions (
  role_id UUID REFERENCES roles(id),
  permission_id UUID REFERENCES permissions(id),
  PRIMARY KEY (role_id, permission_id)
);

CREATE TABLE user_roles (
  user_id UUID REFERENCES users(id),
  role_id UUID REFERENCES roles(id),
  granted_at TIMESTAMP DEFAULT NOW(),
  PRIMARY KEY (user_id, role_id)
);

Hiyerarşik roller ve role composition, her permission kombinasyonu için ayrı bir rol oluşturmayı önleyerek role explosion’ı engeller.

ABAC (Attribute-Based Access Control)#

ABAC, kullanıcı, kaynak, ortam ve aksiyon attribute’larına dayalı dinamik izinler verir; “doktorlar çalışma saatlerinde kendi departmanlarındaki hasta kayıtlarını görüntüleyebilir” gibi bir policy bu şekilde okunur.

interface ABACAttributes {
  user: {
    role: string;
    department: string;
    clearanceLevel: number;
  };
  resource: {
    type: string;
    owner: string;
    classification: string;
  };
  environment: {
    time: Date;
    location: string;
    network: string;
  };
  action: string;
}

const evaluateABACPolicy = (attrs: ABACAttributes): boolean => {
  // Karmaşık kural değerlendirmesi
  if (attrs.user.role === 'doctor' &&
      attrs.resource.type === 'patient_record' &&
      attrs.action === 'read') {

    const currentHour = attrs.environment.time.getHours();
    const isWorkHours = currentHour >= 8 && currentHour <= 18;
    const sameDepartment = attrs.user.department === attrs.resource.owner;

    return isWorkHours && sameDepartment;
  }

  return false;
};

Statik rollerle modellenemeyen karmaşık, contextual erişim gereksinimlerinde işe yarar.

IAM (Identity and Access Management)#

IAM, kapsamlı identity yaşam döngüsü yönetimini kapsar: authentication, authorization, administration ve audit.

Başlıca platformlar arasında AWS IAM, Microsoft Entra ID (eski adıyla Azure AD), Okta ve Auth0 var. Hosted sağlayıcılar aktif kullanıcı ya da aylık aktif kullanıcı başına fiyatlandırır.

Federation, Active Directory veya Google Workspace gibi birden çok identity kaynağını birbirine bağlar.

Principle of Least Privilege#

Principle of least privilege, minimum gerekli izinleri vermek demektir; time-bound erişim (JIT/JEA) ve düzenli erişim incelemeleriyle uygulanır.

// Just-in-Time (JIT) erişim örneği
interface JITAccessRequest {
  userId: string;
  resource: string;
  permissions: string[];
  duration: number; // dakika
  justification: string;
  approver?: string;
}

const grantJITAccess = async (request: JITAccessRequest) => {
  // Hassas kaynaklar için onay gerekli
  if (request.resource.includes('production')) {
    await requireApproval(request);
  }

  // Otomatik revocation ile erişim ver
  await grantPermissions(request.userId, request.permissions);

  // Otomatik revocation'ı schedule et
  setTimeout(async () => {
    await revokePermissions(request.userId, request.permissions);
  }, request.duration * 60 * 1000);

  // Audit için logla
  auditLog.log('JIT_ACCESS_GRANTED', request);
};

Kullanılmayan izinleri kullanım pattern’larına dayalı olarak otomatik kaldırmak en iyi sonucu verir.

Yeni Gelişen Standartlar#

Passkey’ler (FIDO2/WebAuthn)#

Passkey’ler, device-native biometric veya PIN kullanarak passwordless authentication sağlar. Apple, Google ve Microsoft platformlarında yerel destek var; credential’lar bu şirketlerin şifre yöneticileri üzerinden senkronlanıyor.

// Yeni kullanıcı için passkey oluştur
const createPasskey = async () => {
  const credential = await navigator.credentials.create({
    publicKey: {
      challenge: new Uint8Array(32),
      rp: { name: "Uygulamanız", id: "yourapp.com" },
      user: {
        id: new TextEncoder().encode(userId),
        name: userEmail,
        displayName: userName
      },
      pubKeyCredParams: [
        { type: "public-key", alg: -7 },  // ES256
        { type: "public-key", alg: -257 } // RS256
      ],
      authenticatorSelection: {
        authenticatorAttachment: "platform", // Cihaza built-in
        requireResidentKey: true,
        userVerification: "required"
      }
    }
  });

  // Credential ID ve public key'i server'da sakla
  await registerCredential(userId, credential);
};

// Passkey ile authenticate
const authenticateWithPasskey = async () => {
  const assertion = await navigator.credentials.get({
    publicKey: {
      challenge: new Uint8Array(32),
      allowCredentials: userCredentials.map(cred => ({
        type: "public-key",
        id: cred.id
      })),
      userVerification: "required"
    }
  });

  // Assertion'ı server'da verify et
  const isValid = await verifyAssertion(assertion);
  return isValid;
};

FIDO2 Komponentleri#

WebAuthn, güncel Chrome, Safari, Firefox ve Edge tarafından implement edilen W3C standardıdır; CTAP2, platform ile authenticator’lar arasındaki communication protocol’dür.

Authenticator’lar iki tipte gelir:

  • Platform: Cihaza built-in (Touch ID, Face ID, Windows Hello)
  • Roaming: External key’ler (YubiKey, USB/NFC cihazlar)

DPoP (Demonstration of Proof of Possession)#

DPoP, token hırsızlığını ve replay’i önlemek için OAuth2 token’larını client’ın private key’ine bağlar; RFC 9449’da (2023) tanımlanır ve hâlâ erken benimsenme aşamasındadır.

Client, her token kullanımında private key sahipliğini şöyle prove eder:

// DPoP token binding
const createDPoPProof = async (httpMethod: string, url: string, accessToken: string) => {
  const header = {
    typ: 'dpop+jwt',
    alg: 'ES256',
    jwk: publicKeyJWK
  };

  const payload = {
    jti: generateUUID(),
    htm: httpMethod,
    htu: url,
    iat: Math.floor(Date.now() / 1000),
    ath: await sha256(accessToken) // Access token hash
  };

  return jwt.sign(payload, privateKey, { header });
};

// API request ile DPoP proof kullan
const callAPIWithDPoP = async (url: string, accessToken: string) => {
  const dPopProof = await createDPoPProof('GET', url, accessToken);

  return fetch(url, {
    headers: {
      'Authorization': `DPoP ${accessToken}`,
      'DPoP': dPopProof
    }
  });
};

Legacy Authentication Metodları#

Basic Authentication#

Basic Authentication, Authorization header’ında Base64 encoded username:password çiftini her request ile gönderir; bu tek başına düşük güvenlik demektir, o yüzden HTTPS zorunlu.

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

HTTPS zorunlu tutulduğunda basit script’lerde ve legacy sistem entegrasyonlarında hâlâ yerini buluyor.

Digest Authentication#

Digest Authentication artık kullanılmıyor: bozuk MD5 hashing’e dayandığı için yalnızca legacy sistem desteğinde kalmalı, yeni bir sistemde asla tercih edilmemeli. Yerine OAuth2/OIDC ya da en azından modern bir token-based şema koy.

Mutual TLS (mTLS)#

mTLS’de hem client hem server sertifika sunar; bu da service-to-service communication, IoT cihazlar ve yüksek güvenlik ortamları için uygun hale getirir.

// mTLS client konfigürasyonu
const httpsOptions = {
  cert: fs.readFileSync('client-cert.pem'),
  key: fs.readFileSync('client-key.pem'),
  ca: fs.readFileSync('ca-cert.pem'),
  rejectUnauthorized: true
};

const makeSecureRequest = () => {
  return fetch('https://api.example.com/secure', {
    agent: new https.Agent(httpsOptions)
  });
};

Operasyonel yük sertifika yönetimini, rotation’ı ve revocation list’lerini kapsar; bu yükü otomatik mTLS ile service mesh (Istio, Linkerd) üstleniyor.

Takip Edilecek Metrikler#

  • Failed authentication oranı
  • Admin hesaplar için MFA benimsenmesi
  • Şifre reset frekansı (passwordless ile düşmeli)
  • Token refresh oranı ve hataları
  • Güvenlik incident’ları için Mean Time to Detect (MTTD) ve Mean Time to Respond (MTTR)
  • Privilege escalation denemeleri ve olağandışı authentication pattern’ları (coğrafi, zaman tabanlı)
  • Login başarı oranı ve authentication abandonment oranı
  • Authentication sorunlarına bağlı support ticket volume’ü
  • Biometric authentication hata oranı

Kimlik Doğrulama Sistemlerinin Kırıldığı Noktalar#

Authentication Hataları#

  • OAuth2 Karışıklığı: Authentication için OAuth2’yi OIDC olmadan kullanmak (yaygın ve pahalı hata)
  • Şifre Storage: Plaintext veya zayıf hash’lenmiş şifreler saklamak (Argon2id kullan)
  • Token Ömürleri: Rotation olmadan uzun ömürlü token’lar (uzun zaafiyet pencereleri açar)
  • SMS OTP Bağımlılığı: SMS’i hâlâ tek ikinci faktör olarak kullanmak (SIM swapping yaygın)
  • Account Lockout: Eksik veya yetersiz lockout policy’leri

Implementation ve Operasyonel Hatalar#

  • Tek Hata Noktası: Fallback authentication metodunun olmaması
  • Session Yönetimi: Zayıf session handling ve timeout policy’leri
  • Rate Limiting: Authentication endpoint’lerinde eksik rate limiting
  • Bilgi Disclosure: User enumeration’a olanak sağlayan error mesajları
  • Synchronous Validation: Performansı etkileyen blocking token validation
  • Token Revocation: Compromise edilmiş token’ları revoke etme mekanizmasının olmaması
  • Audit Boşlukları: Kapsamlı authentication logging’in eksikliği
  • Key Rotation: Fail olan manuel key rotation süreçleri
  • Sertifika Süresi: Otomatik sertifika yenileme yok
  • Manuel Provisioning: Otomatik kullanıcı yaşam döngüsü yönetiminin eksikliği

Yeni Projeler İçin Varsayılanlar#

Teknik Kararlar#

  • OIDC ile Başla: SAML’i sadece enterprise müşteriler özellikle gerektirdiğinde ekle
  • Passkeys First: Yeni bir projede FIDO2/WebAuthn’i ilk günden implement et
  • Managed Services: Authentication sistemleri inşa etmek yerine Auth0, Okta veya AWS Cognito kullan
  • Sertifika Otomasyonu: Otomatik sertifika yönetimini (Let’s Encrypt, AWS ACM) hemen implement et
  • Token Rotation: Refresh token rotation’ını başlangıçtan design et

Süreç Geliştirmeleri#

  • Güvenlik Eğitimi İlk: Implement etmeden önce ekibi eğit
  • Incremental Rollout: Authentication metod rollout’ları için feature flag’leri kullan
  • Test ve Friction Monitoring: Authentication flow’larını kullanıcı deneyimi etkisi için test et, sonra friction’ı sürekli ölç ve optimize et
  • Düzenli Tatbikatlar: Güvenlik incident response tatbikatlarını üç ayda bir gerçekleştir

Authentication altyapısını kendin inşa etmek yerine satın almak daha isabetlidir; regülatif gereksinimler (SOX, HIPAA, GDPR) de mimari kararın başından itibaren dahil edilmeli, çünkü saklama, loglama ve faktör seçimini kısıtlarlar.

Bu varsayılanlar, enterprise bir müşterinin identity provider’ı yalnızca SAML konuşana, cihaz filosu platform authenticator kaydedemeyene ya da bir regülasyon belirli bir kontrolü şart koşana kadar geçerlidir.

Kaynaklar#

İlgili yazılar