Skip to main content
Web Uygulama Güvenliği · 40 dk okuma

Oturum Yönetiminde En Sık Yapılan Hatalar · Oturum Güvenliği

Güvenli oturum yönetimi yalnızca Secure ve HttpOnly cookie kullanmak değildir. Session'ın üretiminden rotation, timeout, revocation, distributed store ve logout aşamasına kadar bütün yaşam döngüsü korunmalıdır.

SX

SECNODEX Güvenlik Ekibi

Ofansif Güvenlik

Bir kullanıcı uygulamaya MFA ile giriş yapıyor. Backend rastgele ve yeterince uzun bir session ID üretiyor. Cookie üzerinde Secure, HttpOnly ve SameSite=Lax attribute'ları bulunuyor. Bütün trafik HTTPS üzerinden ilerliyor.

Security checklist ilk bakışta tamamlanmış görünüyor.

Ancak kullanıcı login olmadan önce aldığı session ID'yi authentication sonrasında da kullanmaya devam ediyor. Password değiştirildiğinde diğer cihazlardaki session'lar açık kalıyor. Logout yalnızca browser'daki cookie'yi siliyor, server-side session store'daki kayıt geçerliliğini koruyor. Admin role verildiğinde session yeniden üretilmiyor. On beş dakikalık timeout yalnızca cookie'nin Max-Age değerinde bulunuyor, backend aynı session ID'yi saatler sonra kabul ediyor. WebSocket connection ise HTTP session sona erdikten sonra bile açık kalıyor.

Cookie attribute'ları doğru olmasına rağmen oturum güvenli değildir.

Çünkü saldırganın her request'te password veya MFA kodunu yeniden ele geçirmesi gerekmez. Geçerli session secret'ı elde etmesi, sabitlemesi veya uygulamanın iptal edemediği bir token üretmesi yeterli olabilir.

OWASP'ın altını çizdiği kritik gerçek şudur:

Not

Authentication sonrasında session ID, geçici olarak uygulamadaki en güçlü authentication yönteminin güvenlik değerine eşdeğer hale gelir.

Kullanıcı phishing-resistant MFA ile login olmuş olsa bile bearer session cookie çalındığında uygulama yalnızca cookie'yi görüyorsa saldırgan o güçlü authentication sonucunu devralabilir.

Bu nedenle oturum yönetiminde en sık yapılan hatalar cookie ayarlarından ibaret değildir. Sorun çoğunlukla authentication, session lifecycle, authorization, distributed state, logout, federation ve client architecture arasındaki bağlantıların eksik kurulmasıdır.

Kısa cevap

Güvenli oturum yönetimi, tahmin edilemez bir session secret üretmekle başlar fakat orada bitmez. Secret yalnızca güvenli channel üzerinden taşınmalı, login ve privilege change sonrasında rotate edilmeli, server-side idle ve absolute timeout ile sınırlandırılmalı, logout ve risk event'lerinde bütün ilgili state iptal edilmeli, CSRF ve XSS'e karşı ayrı kontroller uygulanmalı, long-lived connection'lar session lifecycle ile senkronize edilmeli ve bütün bu davranışlar source code ile runtime üzerinde doğrulanmalıdır.

Bu rehberde oturum güvenliğini yalnızca klasik JSESSIONID veya PHPSESSID üzerinden değerlendirmeyeceğiz. Server-side session, signed cookie, JWT access token, refresh token, OAuth/OIDC session, mobile client, WebSocket ve distributed session store modellerinin ortak hatalarını birlikte ele alacağız.

Oturum yönetimi gerçekte neyi korur?

HTTP stateless bir protocol'dür. Application, ayrı request'lerin aynı kullanıcıya ve aynı güvenlik context'ine ait olduğunu session mekanizmasıyla belirler.

Bir oturum şu bilgileri doğrudan veya dolaylı taşıyabilir:

  • User identity
  • Authentication zamanı
  • Authentication method
  • MFA veya assurance seviyesi
  • Role ve permission'lar
  • Tenant veya organization context
  • CSRF state
  • Device context
  • Risk score
  • Locale ve application state
  • Impersonation veya delegated access bilgisi
  • Step-up authentication sonucu
  • Session creation ve expiration zamanı

Session ID'nin kendisi yalnızca anlamsız bir reference olabilir. Ancak server-side kayıtta bu ID ile eşleşen context kullanıcının kim olduğunu ve ne yapabileceğini belirler.

Oturum güvenliğinin temel zinciri şöyledir:

Authentication event
        ↓
Session creation
        ↓
Secure transport ve client storage
        ↓
Request-by-request validation
        ↓
Rotation ve privilege transition
        ↓
Idle / absolute expiration
        ↓
Revocation ve logout
        ↓
Cleanup, monitoring ve audit

Bu zincirin tek bir halkası bozulduğunda güçlü password policy, MFA ve doğru authorization kontrolleri etkisizleşebilir.

Authentication, session management ve authorization aynı şey değildir

Bu üç alan birbiriyle bağlantılıdır fakat farklı sorular sorar:

KatmanSoru
AuthenticationBu subject kim?
Session managementBu kimlik sonucu sonraki request'lere nasıl güvenli bağlanıyor?
AuthorizationBu subject, bu object üzerinde bu action'ı yapabilir mi?

Geçerli session, kullanıcının her object üzerinde yetkili olduğu anlamına gelmez. Session içindeki role=admin değerinin doğrulanması da object-level authorization'ın yerine geçmez.

Oturum ile authorization arasındaki bu sınır özellikle IDOR açıklarında görünür. Kullanıcı tamamen geçerli bir session ile başka müşteriye ait object'e ulaşabilir. Bu kök nedeni IDOR açığı neden hâlâ bu kadar yaygın? yazımızda ayrıntılı ele alıyoruz.

Önce session modelini doğru adlandırın

Birçok uygulamada “JWT kullanıyoruz, session yok” denir. Gerçekte kullanıcı login olduktan sonra bir secret veya proof sonraki request'lerde kimliği sürdürüyorsa session benzeri bir güven ilişkisi vardır.

Server-side session

Client çoğunlukla opaque bir session ID taşır. Identity, role, timeout ve diğer state server-side session store'da tutulur.

Cookie: __Host-session=opaque_random_value

Server-side store:
opaque_random_value → {
  userId,
  tenantId,
  authTime,
  assuranceLevel,
  createdAt,
  lastSeenAt,
  expiresAt
}

Avantajı merkezi revocation ve state update kolaylığıdır. Dezavantajı session store availability, consistency ve ölçekleme gereksinimidir.

Client-side signed veya encrypted session

Session state cookie içinde taşınır. Integrity için signature, confidentiality gerekiyorsa encryption uygulanır.

Signed olmak encrypted olmak değildir. Client veriyi okuyabilir fakat değiştirdiğinde signature geçersiz olur. Framework dokümantasyonu bu ayrımı açıkça belirtmiyorsa developer cookie içindeki role, e-mail veya personal data'nın gizli olduğunu sanabilir.

Client-side modelde logout ve global revocation daha zordur. Server token family, user security version veya denylist gibi ek state tutmuyorsa çalınmış eski cookie expiration'a kadar tekrar kullanılabilir.

Access token ve refresh token

OAuth access token, resource server'a belirli scope ile erişim sağlar. Refresh token yeni access token almak için kullanılır.

Access token'ın varlığı kullanıcının o anda uygulamanın başında olduğunu kanıtlamaz. NIST, access ve refresh token'ların authentication session sona erdikten sonra da geçerli olabileceğini özellikle belirtir.

Browser session ve IdP session

OIDC veya SAML kullanıldığında en az iki farklı session bulunabilir:

  • Identity Provider session
  • Relying Party veya application session

Application session sona erdiğinde IdP session açık olabilir. Kullanıcı tekrar uygulamaya geldiğinde IdP yeni assertion üretip kullanıcıyı görünürde password sormadan yeniden login edebilir. Tersi de mümkündür. IdP logout, application session'ını otomatik olarak kapatmayabilir.

Bu nedenle “SSO'dan logout olduk” cümlesi hangi session'ın kapandığını açıklamaz.

Hata 1: Tahmin edilebilir veya yetersiz entropy'ye sahip session ID üretmek

Session ID üretimi için timestamp, user ID, IP address, incremental counter, UUID v1 veya Math.random() gibi predictable girdiler kullanılmamalıdır.

Riskli örnek:

const sessionId =
  user.id + "-" + Date.now() + "-" + Math.random().toString(16);

Bu değer uzun görünebilir. Fakat uzunluk entropy değildir. User ID bilinebilir, timestamp dar bir aralıkta tahmin edilebilir ve Math.random() cryptographic randomness sağlamaz.

NIST SP 800-63B-4, session binding secret için approved random bit generator ve en az 64 bit uzunluk ister. OWASP ise custom session ID üretilmesi gerekiyorsa CSPRNG ile en az 128 bit değer önerir.

Güvenli Node.js örneği:

import crypto from "node:crypto";

export function generateSessionId() {
  return crypto.randomBytes(32).toString("base64url");
}

Bu örnek 256 bit random input üretir. Yine de mümkün olduğunda framework'ün güncel ve test edilmiş session management implementation'ı kullanılmalıdır. Custom generator yazmak yalnızca random değer üretmek değildir. Uniqueness, encoding, storage, timing behavior ve collision handling de doğru olmalıdır.

Session ID anlamsız olmalı

Client'a verilen session ID içinde şu bilgiler bulunmamalıdır:

  • User ID
  • Username veya e-mail
  • Role
  • Tenant ID
  • IP address
  • Authentication method
  • Internal server ID
  • Expiration timestamp
  • Personal data

Opaque reference kullanıldığında business context server-side tutulur.

Session ID adı güvenlik kontrolü değildir

JSESSIONID yerine id kullanmak framework fingerprinting bilgisini azaltabilir. Fakat session hijacking riskini çözmez. Asıl güvenlik değerini ID'nin entropy'si, lifecycle'ı ve korunma biçimi belirler.

Hata 2: Session ID'yi URL, query string veya log'a taşımak

Şu URL ciddi bir tasarım hatasıdır:

https://app.example.com/dashboard?session=eyJ...

URL içindeki token şu alanlara sızabilir:

  • Browser history
  • Bookmark
  • Reverse proxy log'u
  • Web server access log'u
  • APM ve tracing platformu
  • Analytics
  • Screenshot
  • Support kaydı
  • Referer header
  • Search engine veya link preview
  • E-mail security gateway

Session ID exchange için tek bir mekanizma seçilmeli ve application diğer mekanizmalardan gelen ID'leri kabul etmemelidir.

Örneğin uygulama cookie kullanıyorsa şu request reddedilmelidir:

GET /account?JSESSIONID=attacker-chosen-value HTTP/1.1
Host: app.example.com
Cookie: JSESSIONID=valid-cookie-value

Backend query parameter'ı cookie'den öncelikli kabul ederse attacker-controlled session fixation veya session confusion oluşabilir.

Authorization header da otomatik olarak güvenli değildir

Bearer token'ın Authorization header içinde taşınması URL'den daha doğru olabilir. Ancak reverse proxy, debug middleware veya APM agent bütün request header'larını kaydediyorsa token yine sızar.

Logging pipeline şu alanları default olarak redact etmelidir:

  • Cookie
  • Set-Cookie
  • Authorization
  • CSRF token
  • Refresh token
  • Password reset token
  • MFA recovery code

Hata 3: Cookie attribute'larını eksik veya yanlış kullanmak

Güvenli bir first-party browser session cookie için güçlü baseline şöyledir:

Set-Cookie: __Host-session=<opaque-value>; Path=/; Secure; HttpOnly; SameSite=Lax

Use case izin veriyorsa SameSite=Strict daha dar koruma sağlar. Cross-site federation veya embedded use case gerekiyorsa cookie tasarımı ayrıca değerlendirilmelidir.

Secure neyi sağlar?

Secure, browser'ın cookie'yi yalnızca HTTPS request'lerde göndermesini sağlar.

Application'ın bütün sayfaları HTTPS üzerinden yayınlansa bile Secure kaldırılmamalıdır. Yanlış bir HTTP linki, subdomain veya downgrade path'i session secret'ın cleartext request'e eklenmesine yol açabilir.

TLS yalnızca login endpoint'inde değil, session'ın bütün yaşam döngüsünde kullanılmalıdır. HSTS de HTTPS kullanımını browser tarafında zorlamaya yardımcı olur.

HttpOnly neyi sağlar, neyi sağlamaz?

HttpOnly, client-side JavaScript'in cookie değerini document.cookie üzerinden okumasını engeller.

Fakat XSS'i etkisiz hale getirmez. Malicious script cookie'yi okuyamasa bile victim browser üzerinden authenticated request gönderebilir, DOM'daki hassas veriyi okuyabilir veya business action gerçekleştirebilir.

Dolayısıyla:

HttpOnly = Session cookie confidentiality için önemli defense in depth
HttpOnly ≠ XSS çözümü

SameSite neyi sağlar?

SameSite, cross-site context'te cookie'nin gönderilmesini sınırlandırarak CSRF riskini azaltır.

  • Strict: Cookie cross-site navigation dahil en dar biçimde sınırlandırılır
  • Lax: Top-level safe navigation için daha uyumludur
  • None: Cross-site gönderime izin verir ve Secure gerektirir

Ancak same-site ile same-origin aynı şey değildir. app.example.com ve saldırganın kontrol ettiği evil.example.com farklı origin fakat belirli koşullarda aynı site kabul edilebilir. Subdomain takeover veya daha düşük güven seviyesindeki sibling application, SameSite varsayımını zayıflatabilir.

Bu nedenle state-changing request'lerde CSRF token, Origin veya Fetch Metadata doğrulaması gibi kontroller gerekli olabilir.

Domain neden mümkün olduğunca yazılmamalı?

Şu cookie bütün subdomain'lere gönderilebilir:

Set-Cookie: session=<value>; Domain=example.com; Path=/; Secure; HttpOnly

Marketing, support, legacy veya user-content subdomain'lerinden biri compromise olduğunda session scope genişlemiş olur.

Host-only cookie için Domain attribute'u hiç yazılmamalıdır.

__Host- prefix neden değerlidir?

__Host- ile başlayan cookie:

  • Secure olmalıdır
  • Domain içermemelidir
  • Path=/ kullanmalıdır

Browser bu koşullara uymayan __Host- cookie'yi kabul etmez. Böylece security property yalnızca application configuration'a değil, browser enforcement'a da bağlanır.

Path güvenlik sınırı değildir

Path=/admin cookie'nin yalnızca belirli request path'lerinde gönderilmesini sağlar. Aynı host üzerindeki farklı application'lar arasında güçlü isolation sağlamaz. Aynı host'taki script başka path için cookie set edebilir.

Farklı security level'a sahip uygulamalar mümkün olduğunda ayrı host ve cookie boundary kullanmalıdır.

Hata 4: Reverse proxy arkasında Secure cookie'yi yanlış yapılandırmak

Production'da TLS load balancer üzerinde terminate edilebilir. Application ile reverse proxy arasındaki bağlantı HTTP olduğunda framework request'i insecure sanabilir ve Secure cookie üretmeyebilir.

Takımlar bunu çözmek için bütün X-Forwarded-* header'larına koşulsuz güvenebilir. Bu kez Internet'ten doğrudan gelen sahte X-Forwarded-Proto: https header'ı security decision'ı etkileyebilir.

Doğru tasarım:

  • Application yalnızca bilinen reverse proxy'lerden gelen forwarded header'ları kabul eder
  • Direct backend access network seviyesinde engellenir
  • Trusted proxy count veya CIDR açıkça tanımlanır
  • HTTPS redirect ve cookie behavior integration test ile doğrulanır
  • Health check ve internal route'lar security configuration'ı değiştirmez

Express için trust proxy değeri deployment topology'ye özel olmalıdır. İnternetten gelen her proxy header'ını güvenilir kabul eden geniş configuration kullanılmamalıdır.

Hata 5: Authentication sonrasında session ID'yi rotate etmemek

Session fixation saldırısında attacker, authentication öncesi session ID'yi bilir veya victim browser'a yerleştirir. Victim aynı ID ile login olduğunda session authenticated hale gelir. Attacker bildiği ID'yi kullanarak victim session'ını devralır.

Temel kapanış kriteri:

Pre-authentication session ID ≠ Post-authentication session ID

Rotation şu olaylarda değerlendirilmelidir:

  • Password ile login
  • SSO callback
  • MFA tamamlanması
  • Anonymous session'dan authenticated session'a geçiş
  • Privilege elevation
  • Admin impersonation başlangıcı ve bitişi
  • Tenant veya organization context değişimi
  • Risk event sonrasında reauthentication
  • Password reset ile otomatik login

Yalnızca login formunda rotation yapmak yeterli değildir. OIDC callback veya mobile token exchange farklı code path kullanıyorsa eski session korunabilir.

Anonymous state nasıl taşınmalı?

E-commerce uygulaması anonymous cart bilgisini login sonrasında korumak isteyebilir. Eski session'ı aynen authenticate etmek yerine yeni session oluşturulmalı ve yalnızca güvenli, allowlist edilmiş state taşınmalıdır.

Taşınabilir:
- cart item reference
- locale
- non-sensitive UI preference

Taşınmamalı:
- pre-auth role
- return URL without validation
- CSRF secret
- privilege flag
- untrusted redirect state
- attacker-controlled identity context

Spring Security örneği

Spring Security güncel Servlet environment'ında session fixation'a karşı varsayılan olarak session ID'yi değiştirir. Custom authentication flow yazıldığında bu protection'ın bypass edilmediği doğrulanmalıdır.

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .sessionManagement(session -> session
            .sessionFixation(fixation -> fixation.changeSessionId())
            .maximumSessions(3)
            .maxSessionsPreventsLogin(false)
        )
        .logout(logout -> logout
            .invalidateHttpSession(true)
            .clearAuthentication(true)
            .deleteCookies("__Host-session")
        );

    return http.build();
}

changeSessionId() seçimi session fixation protection sağlar. Ancak uygulamanın custom filter, authentication success handler, explicit SecurityContextRepository veya farklı SSO flow kullandığı durumda integration test zorunludur.

Express örneği

app.post("/login", async (req, res, next) => {
  try {
    const user = await authenticate(req.body);

    req.session.regenerate((regenerateError) => {
      if (regenerateError) return next(regenerateError);

      req.session.userId = user.id;
      req.session.tenantId = user.tenantId;
      req.session.authTime = Date.now();
      req.session.assuranceLevel = "aal2";

      req.session.save((saveError) => {
        if (saveError) return next(saveError);
        res.status(204).end();
      });
    });
  } catch (error) {
    next(error);
  }
});

Rotation tamamlanmadan authenticated response dönülmemeli, session store write hatası fail-open davranışa dönüşmemelidir.

Hata 6: Privilege değiştiğinde mevcut session'ı olduğu gibi sürdürmek

Kullanıcı login olduğunda role=user bilgisi session'a kopyalanıyor. Daha sonra admin panelinden kullanıcıya role=admin atanıyor.

İki risk oluşur:

  1. 1Eski session role bilgisini cache'lediği için yeni yetki beklenmedik anda geçerli olmaz
  2. 2Compromise edilmiş eski session yeni privilege'i sessizce devralır

Privilege artışı güvenlik boundary değişimidir. Session'ın:

  • Yeniden authenticate edilmesi
  • Session ID'sinin rotate edilmesi
  • auth_time, amr ve assurance bilgisinin güncellenmesi
  • Eski privilege context'inin invalid hale gelmesi
  • Diğer cihazlardaki session'ların policy'ye göre sonlandırılması

gerekebilir.

Role azalması daha da kritiktir. Database'den permission kaldırılmış olsa bile session içindeki stale claim saatlerce kabul ediliyorsa revocation çalışmıyor demektir.

User security version pattern

Merkezi revocation için user kaydında version tutulabilir:

users:
  id: 42
  security_version: 18

session:
  user_id: 42
  security_version: 18

Password change, role change veya “logout all devices” sırasında security_version artırılır. Her request'te veya kısa süreli güvenilir cache üzerinden session version ile current version karşılaştırılır.

async function validateSession(session) {
  const currentVersion = await userSecurityVersion(session.userId);

  if (session.securityVersion !== currentVersion) {
    throw new SessionRevokedError();
  }
}

Bu pattern tek başına yeterli değildir. Cache invalidation, replica consistency ve high-volume lookup maliyeti tasarlanmalıdır. Ancak “signed token olduğu için iptal edilemez” yaklaşımından daha kontrol edilebilir bir model sağlar.

Hata 7: Timeout'u yalnızca browser cookie'sine bırakmak

Cookie expiration ile server-side session expiration aynı şey değildir.

Attacker daha önce çaldığı session ID'yi browser cookie jar dışında saklayabilir. Browser cookie'yi sildiğinde server session hâlâ geçerliyse ID doğrudan replay edilebilir.

Timeout server-side enforce edilmelidir.

Idle timeout

Kullanıcının belirli süre etkinlik göstermemesi halinde session sona erer.

idle_expired =
  now - last_meaningful_activity > idle_timeout

Her background polling, analytics veya health request kullanıcı aktivitesi sayılmamalıdır. Aksi halde browser açık kaldığı sürece session hiç idle olmaz.

Absolute timeout

Session ne kadar aktif olursa olsun authentication veya son reauthentication sonrasında belirli sürede sona erer.

absolute_expired =
  now - authenticated_at > absolute_timeout

rolling: true veya her response'ta cookie expiration yenilemek yalnızca idle benzeri behavior üretir. Absolute timeout ayrıca tutulmalıdır.

Renewal timeout

Belirli aralıklarla session ID rotate edilir. Eski ve yeni ID arasında kısa bir overlap window gerekebilir. Parallel request ve network latency nedeniyle eski ID anında kapatılırsa kullanıcı deneyimi bozulabilir. Overlap çok uzun tutulursa çalınmış eski ID yaşamaya devam eder.

Timeout değeri nasıl seçilir?

Tek bir evrensel sayı yoktur. Şunlar dikkate alınmalıdır:

  • Application sensitivity
  • User role
  • Managed veya unmanaged device
  • Public veya restricted environment
  • Transaction impact
  • MFA ve assurance seviyesi
  • Device-bound session capability
  • Kullanıcı deneyimi

NIST SP 800-63B-4, AAL2 için overall timeout'un 24 saati, inactivity timeout'un bir saati aşmamasını önerir. AAL3 için overall timeout en fazla 12 saat, inactivity timeout ise 15 dakika olarak verilir. Bunlar her ticari uygulamaya kopyalanacak sabit değerler değil, risk temelli tasarım için güçlü referanslardır.

Timeout response davranışı

Session sona erdiğinde:

  • Protected data dönülmemeli
  • Session store kaydı geçersiz olmalı
  • Client cookie expire edilmeli
  • WebSocket ve SSE connection kapatılmalı
  • Unsaved work için güvenli UX sağlanmalı
  • Login sonrası return URL allowlist ile doğrulanmalı
  • API tutarlı 401 veya uygun authentication response üretmeli

Hata 8: Logout'u yalnızca client-side işlem sanmak

Şu kod güvenli logout değildir:

localStorage.removeItem("token");
window.location.href = "/login";

Client yalnızca kendi kopyasını siler. Çalınmış token veya başka cihazdaki session yaşamaya devam eder.

Güvenli logout en az şu adımları içermelidir:

  1. 1Server-side session invalidate edilir
  2. 2Session cookie doğru attribute'larla expire edilir
  3. 3İlgili access ve refresh token policy'ye göre revoke edilir
  4. 4Long-lived connection'lar kapatılır
  5. 5Sensitive cached response'lar için uygun cache policy uygulanır
  6. 6Audit event üretilir
  7. 7Kullanıcıya logout sonucu açıkça gösterilir

Express örneği:

app.post("/logout", requireCsrfProtection, (req, res, next) => {
  req.session.destroy((error) => {
    if (error) return next(error);

    res.clearCookie("__Host-session", {
      path: "/",
      secure: true,
      httpOnly: true,
      sameSite: "lax"
    });

    res.setHeader("Clear-Site-Data", "\"cache\", \"storage\"");
    res.status(204).end();
  });
});

Cookie silinirken name ve path başta olmak üzere scope değerleri orijinal cookie ile eşleşmelidir. Clear-Site-Data güçlü ve geniş etkili bir header'dır. Aynı origin'deki ihtiyaç duyulan storage'ı da temizleyebilir. Kullanım kararı application behavior ile test edilmelidir.

Logout endpoint'i CSRF'e karşı korunmalı mı?

Logout CSRF, victim'ı hesabından çıkararak availability ve workflow etkisi yaratabilir. Daha önemlisi bazı uygulamalar logout sonrası attacker-controlled account'a login yönlendirmesiyle login CSRF zincirine düşebilir.

Logout state-changing action olarak POST ve uygun CSRF protection ile tasarlanmalıdır. GET /logout linki crawler, prefetch veya cross-site image request ile tetiklenebilir.

“Logout all devices” gerçekten bütün session'ları kapatmalı

Bu işlem:

  • Bütün server-side session'ları
  • Refresh token family'lerini
  • Remember-me token'larını
  • Mobile session'ları
  • Active WebSocket connection'ları
  • Federated application session'larını

policy kapsamına göre revoke etmelidir.

Yalnızca current browser cookie'sini silen “tüm cihazlardan çıkış” butonu yanlış güven hissi üretir.

Hata 9: Password reset, MFA değişikliği ve account recovery sonrasında session'ları açık bırakmak

Account recovery bir risk event'idir. Password reset başarılı olduktan sonra attacker'ın önceden ele geçirdiği session devam ediyorsa yeni password kullanıcıyı korumaz.

Session policy şu event'leri ayrı ayrı ele almalıdır:

  • Password change
  • Forgot password completion
  • MFA enrollment
  • MFA removal
  • Recovery e-mail değişikliği
  • Phone number değişikliği
  • Passkey ekleme veya silme
  • Role veya tenant membership değişikliği
  • Account disable
  • Fraud flag
  • Device lost bildirimi

Her event aynı aksiyonu gerektirmeyebilir. Örneğin kullanıcı kendi password'ünü normal flow içinde değiştirirken current session reauthenticated olarak korunabilir, diğer session'lar revoke edilebilir. Help desk recovery sonrasında daha agresif global revocation uygulanabilir.

Karar açık policy olmalıdır. Developer'ın ilgili endpoint'te neyi hatırladığına bırakılmamalıdır.

Hata 10: SameSite kullandığı için CSRF'in çözüldüğünü sanmak

SameSite=Lax veya Strict önemli defense in depth sağlar. Ancak aşağıdaki durumlar ayrıca değerlendirilmelidir:

  • Same-site sibling subdomain compromise
  • Subdomain takeover
  • Login CSRF
  • Client-side CSRF
  • State-changing GET endpoint
  • Cross-site SSO veya embedded flow nedeniyle SameSite=None
  • Browser veya non-browser client farklılıkları
  • CORS ve credential configuration

Cookie tabanlı authenticated application'da state-changing request'ler için:

  • Framework'ün built-in CSRF protection'ı
  • Synchronizer token
  • Session-bound signed double-submit cookie
  • Origin ve Referer validation
  • Fetch Metadata policy
  • User interaction veya reauthentication

use case'e göre birlikte kullanılabilir.

CSRF token session'a bağlanmalı

Naive double-submit pattern'de attacker kendi cookie ve request değerini victim'a enjekte edebiliyorsa protection bypass edilebilir. Token session'a cryptographic olarak bağlanmalı veya server-side synchronizer token kullanılmalıdır.

POST kullanmak CSRF'i çözmez

Browser cross-site form ile POST request gönderebilir. Content-Type ve custom header gereksinimleri attack surface'i daraltabilir fakat tasarım bilinçli olmalıdır.

Hata 11: HttpOnly kullandığı için XSS'in session'ı etkileyemeyeceğini düşünmek

HttpOnly session cookie'yi JavaScript'ten gizler. Ancak XSS payload victim'ın browser'ında çalıştığı için:

  • Authenticated API request gönderebilir
  • Response body okuyabilir
  • Transaction başlatabilir
  • CSRF token'ı DOM'dan alabilir
  • Kullanıcı input'unu keylog edebilir
  • Session açıkken data exfiltration yapabilir
  • Yeni API key veya OAuth grant oluşturabilir

Dolayısıyla session cookie çalınmadan session riding yapılabilir.

Oturum güvenliği için XSS prevention, output encoding, safe DOM API, Content Security Policy, dependency governance ve third-party script kontrolü ayrı katmanlardır.

localStorage neden kritik?

NIST, session secret'ların XSS exposure nedeniyle HTML5 Local Storage gibi güvensiz konumlara yerleştirilmemesini önerir.

localStorage:

  • Origin altındaki JavaScript tarafından okunabilir
  • Browser restart sonrasında kalıcıdır
  • Built-in HttpOnly eşdeğeri yoktur
  • XSS sırasında toplu token theft'e açıktır

Browser tabanlı uygulamalarda Backend for Frontend yaklaşımı, token'ı JavaScript'e vermeden server-side saklayıp browser'a HttpOnly session cookie sunabilir. Bu architecture her sisteme otomatik çözüm değildir. CSRF, BFF compromise ve backend session lifecycle yine ele alınmalıdır.

Hata 12: JWT imzalı olduğu için otomatik olarak güvenli ve iptal edilebilir sanmak

JWT yalnızca bir format ve claim container'dır. Güvenlik şu kararlara bağlıdır:

  • Algorithm allowlist
  • Signature validation
  • Key management
  • iss, aud, exp, nbf validation
  • Token type ayrımı
  • Scope ve authorization
  • Client storage
  • Revocation
  • Rotation
  • Replay detection

Signed JWT encrypted değildir. Payload çoğunlukla okunabilir. Personal data veya secret claim içine konulmamalıdır.

exp revocation değildir

Bir access token 60 dakika geçerliyse user logout olduktan sonra token kalan sürede kullanılabilir. Resource server yalnızca signature ve exp kontrol ediyorsa merkezi logout etkisizdir.

Çözümler:

  • Kısa ömürlü access token
  • Refresh token rotation
  • Sender-constrained token
  • Token introspection
  • Revocation list
  • User security version
  • Key veya grant bazlı revocation

Her çözümün availability ve latency etkisi vardır.

Refresh token rotation nasıl çalışmalı?

RFC 9700, public client'larda refresh token replay'i tespit etmek için sender-constrained refresh token veya refresh token rotation kullanılmasını ister.

Rotation akışı:

RT-1 kullanılır
  ↓
RT-1 invalid
RT-2 active
  ↓
Eski RT-1 yeniden görülür
  ↓
Token family compromise kabul edilir
  ↓
RT-2 ve ilişkili grant revoke edilir

Sadece yeni refresh token üretip eskiyi expiration'a kadar geçerli bırakmak rotation değildir.

Concurrent refresh request'ler için kısa grace window tasarlanabilir. Ancak replay detection'ı etkisizleştirecek geniş overlap kullanılmamalıdır.

Hata 13: Access token, refresh token ve browser session yaşam döngülerini birbirinden koparmak

Modern application şu bileşenlere sahip olabilir:

  • Browser session cookie
  • BFF session
  • OAuth access token
  • Refresh token
  • IdP session
  • API gateway cache
  • Mobile device token

Kullanıcı logout olduğunda hangisinin sona erdiği açık değilse çalınmış credential bir katmanda yaşamaya devam eder.

Bir session inventory hazırlanmalıdır:

StateOwnerStorageTimeoutRotationRevocation
Browser cookieApplicationBrowserIdle + absoluteLogin ve elevationCookie expire + server invalidate
Server sessionApplicationRedis veya databaseServer-enforcedSession IDStore delete
Access tokenAuthorization ServerClient memory/BFFShortYeni issuanceIntrospection veya short expiry
Refresh tokenAuthorization ServerProtected client storeInactivity + absoluteHer kullanımFamily revoke
IdP sessionIdentity ProviderBrowser + IdPIdP policyReauthenticationIdP logout
WebSocket stateRealtime serviceProcess/distributed mapConnection + auth expiryProtocol-specificConnection close

Bu tablo olmadan “logout çalışıyor” testi yalnızca UI davranışını doğrular.

Hata 14: Federation logout ve reauthentication'ı yanlış varsaymak

OIDC login sonrasında RP kendi local session'ını oluşturur. IdP ve RP session'ları bağımsız sona erebilir.

Şu akış sık görülür:

  1. 1Kullanıcı application'da logout olur
  2. 2Local RP session kapanır
  3. 3IdP session açık kalır
  4. 4Kullanıcı login'e basar
  5. 5IdP mevcut session ile yeni authorization response üretir
  6. 6Kullanıcı password sorulmadan tekrar login olur

Bu davranış teknik olarak protocol ve policy ile uyumlu olabilir. Ancak kullanıcı “hesabımdan tamamen çıktım” bekliyorsa UX ve risk modeli yanlış eşleşir.

Reauthentication gerektiren kritik işlemde RP:

  • auth_time
  • max_age
  • acr
  • amr
  • IdP'nin gerçekten yeni authentication yapıp yapmadığı

gibi bilgileri değerlendirmelidir.

OpenID Connect Back-Channel Logout, IdP'nin RP'lere logout token göndererek ilgili session'ların kapatılmasını sağlar. Ancak endpoint authentication, sid veya sub mapping, replay protection, delivery failure ve retry behavior doğru uygulanmalıdır.

Hata 15: Distributed session store'u yalnızca performans bileşeni görmek

Session store bir security control'dür. Şu davranışlar önemlidir:

  • ID uniqueness
  • Atomic create ve rotate
  • TTL enforcement
  • Delete consistency
  • Concurrent update behavior
  • Encryption at rest
  • Network authentication
  • Tenant isolation
  • Backup retention
  • Failover
  • Replica lag
  • Observability
  • Secret ve PII minimization

Default in-memory store production için uygun olmayabilir

Express MemoryStore, production için tasarlanmamıştır. Tek process dışına ölçeklenmez ve memory problemi oluşturabilir. Birden fazla instance kullanıldığında request farklı instance'a gittiğinde session kaybolabilir veya sticky session bağımlılığı oluşur.

Fail-open davranışı

Session store unavailable olduğunda application:

  • Bütün request'leri anonymous mı kabul ediyor?
  • Cached identity ile devam mı ediyor?
  • Yeni session yaratıyor mu?
  • Logout'u başarılı gösterip delete işlemini kaybediyor mu?

Security-sensitive application store doğrulanamadığında authenticated access'i fail-closed ele almalıdır. Ancak bu availability etkisi circuit breaker, capacity ve incident plan ile yönetilmelidir.

Replica lag revocation'ı bozabilir

Logout primary store'da session'ı siler. Authentication read'i stale replica'dan yapılıyorsa kısa süre önce revoke edilen session kabul edilebilir.

Revocation path için consistency requirement açık olmalıdır. “Database eventually consistent” teknik detay değil, session replay window belirleyen güvenlik kararıdır.

Hata 16: Parallel request ve race condition'ları test etmemek

Browser tek seferde birden fazla request gönderebilir. SPA initial load, mobile retry, HTTP/2 multiplexing ve background API çağrıları aynı session state üzerinde yarışabilir.

Örnek:

  1. 1Request A session'a mfaVerified=true yazar
  2. 2Request B eski session snapshot'ını yükler
  3. 3Request A store'a kaydeder
  4. 4Request B eski snapshot'ı daha sonra kaydederek değişikliği ezer

Tersi durumda revoked veya düşük privilege state eski request tarafından geri yazılabilir.

Express dokümantasyonu, gereksiz resave: true kullanımının parallel request'lerde session değişikliklerini overwrite edebileceğini açıkça belirtir.

State update için:

  • Atomic operation
  • Version veya compare-and-set
  • Field-level update
  • Distributed lock yalnızca gerekli yerde
  • Idempotency
  • Monotonic security state

değerlendirilmelidir.

Security state mümkün olduğunda geriye gitmemelidir:

revoked=true → daha eski request ile revoked=false olamaz
mfa_required=true → stale write ile kaldırılamaz
security_version=19 → 18'e dönemez

Hata 17: Session'ı IP address'e katı biçimde bağlamak

IP binding ilk bakışta session theft'i engeller. Ancak mobile network, corporate proxy, VPN, NAT64 ve ISP routing nedeniyle meşru kullanıcının IP address'i değişebilir.

Katı binding:

  • Kullanıcıyı gereksiz logout eder
  • Help desk yükü oluşturur
  • Saldırgan aynı NAT veya proxy arkasındaysa güvenlik sağlamaz
  • Privacy ve data retention riskini artırır

IP, User-Agent, device signal, geolocation ve velocity session monitoring için risk sinyali olabilir. NIST de session monitoring kapsamında bu sinyallerin değerlendirilebileceğini, privacy etkilerinin ayrıca ele alınması gerektiğini belirtir.

Daha dengeli model:

Risk low      → Session devam
Risk medium   → Step-up authentication
Risk high     → Session suspend veya revoke

Karar yalnızca tek değişken üzerinden verilmemelidir.

Hata 18: Kritik işlem için eski authentication sonucuna güvenmek

Kullanıcı üç gün önce login olmuş olabilir. Session hâlâ geçerli diye şu işlemler aynı assurance ile yapılmamalıdır:

  • Password değiştirme
  • MFA kaldırma
  • Recovery e-mail değiştirme
  • Yeni API key üretme
  • Banka hesabı veya ödeme bilgisi değiştirme
  • Admin role verme
  • Sensitive export
  • Account deletion
  • High-value transaction

Step-up authentication yalnızca login ekranını tekrar göstermek değildir. Uygulama:

  • Son gerçek authentication zamanını
  • Kullanılan authenticator'ları
  • Assurance level'ı
  • Transaction context'ini
  • Session risk signal'larını

kontrol etmelidir.

Örnek policy:

Action: Add new payment beneficiary
Required assurance: AAL2-equivalent
Maximum auth age: 5 minutes
Phishing-resistant factor: Required for privileged users
Session rotation after step-up: Yes
Replay protection: Required

Hata 19: Concurrent session politikasını hiç tanımlamamak

Bir kullanıcı aynı anda sınırsız sayıda session oluşturabiliyorsa çalınmış session uzun süre fark edilmeyebilir.

Tek doğru çözüm “her kullanıcıya bir session” değildir. Bu yaklaşım mobile, desktop ve multi-device kullanımını bozabilir.

Policy role ve risk bazlı olabilir:

  • Consumer user: Birden fazla session, görünür device listesi
  • Finance approver: Düşük concurrent session limiti
  • Privileged admin: Managed device ve daha dar limit
  • Service account: Interactive session yasak
  • Support impersonation: Ayrı, kısa ve audit edilen session

Kullanıcıya şu bilgileri gösteren session management ekranı değerlidir:

  • Device veya client türü
  • Yaklaşık location
  • İlk login zamanı
  • Son aktivite
  • Authentication method
  • Current session işareti
  • Tekil revoke butonu
  • Bütün session'ları sonlandırma

Session listesinde raw session ID kesinlikle gösterilmemelidir.

Hata 20: WebSocket connection'ı handshake sonrasında sonsuza kadar güvenilir saymak

WebSocket upgrade request authenticated olduğunda connection uzun süre açık kalabilir. HTTP session timeout veya logout gerçekleşse bile server connection'ı kapatmıyorsa eski authorization yaşamaya devam eder.

Güvenli WebSocket oturum yönetimi:

  • Origin allowlist doğrulaması
  • Handshake authentication
  • Connection ile session mapping
  • Session expiration kontrolü
  • Logout event'inde connection close
  • Message-level authorization
  • Tenant ve object context doğrulaması
  • Reauthentication veya token rotation
  • Connection ve message rate limit
  • Security event logging

Upgrade sırasında admin olan kullanıcı daha sonra role kaybettiğinde açık connection üzerinden admin message göndermeye devam etmemelidir.

function authorizeMessage(connection, message) {
  const currentSession = sessionStore.get(connection.sessionId);

  if (!currentSession || currentSession.revoked) {
    connection.close(1008, "Session expired");
    return false;
  }

  return policyEngine.isAllowed({
    subject: currentSession.userId,
    tenant: currentSession.tenantId,
    action: message.type,
    object: message.objectId
  });
}

Connection authentication, message-level authorization'ın yerine geçmez.

Hata 21: Sensitive response'ları cache'lenebilir bırakmak

Session sona erdikten sonra browser back button ile hassas sayfanın görünmesi her zaman server session'ın geçerli olduğu anlamına gelmez. Browser cached response gösterebilir.

Sensitive response'larda use case'e uygun olarak şu header değerlendirilebilir:

Cache-Control: no-store
Pragma: no-cache

no-store, browser ve intermediary cache'in response'u saklamamasını ister. Ancak service worker, application-level cache ve downloaded file ayrı değerlendirilmelidir.

Logout sonrasında yalnızca DOM'u temizlemek yeterli değildir. SPA memory state, IndexedDB, service worker cache ve offline storage içinde hassas veri kalabilir.

Hata 22: Session ID'yi düz metin loglamak

Session lifecycle izlenmelidir fakat raw session secret loglanmamalıdır.

Güvenli correlation için ayrı bir logging key ile HMAC tabanlı fingerprint üretilebilir:

import crypto from "node:crypto";

function sessionLogReference(sessionId, loggingKey) {
  return crypto
    .createHmac("sha256", loggingKey)
    .update(sessionId)
    .digest("hex")
    .slice(0, 20);
}

Bu reference operational correlation sağlar. Application authentication için kullanılamaz. Logging key session signing key'den ayrı tutulmalıdır.

Loglanabilecek event'ler:

  • Session created
  • Session rotated
  • Reauthentication completed
  • Privilege changed
  • Session revoked
  • Logout
  • Timeout
  • Concurrent session limit
  • Token replay
  • Impossible travel veya risk event
  • WebSocket session closure

Log içinde password, token, cookie, CSRF secret veya sensitive personal data bulunmamalıdır.

Hata 23: Signed client-side session'ın freshness sağladığını varsaymak

Client-side session cookie cryptographic signature ile korunabilir. Signature, client'ın veriyi fark edilmeden değiştirmesini engeller. Ancak tek başına şu garantileri sağlamaz:

  • Confidentiality
  • Freshness
  • Logout sonrası revocation
  • Tek kullanımlık olma
  • Permission değişikliğinin anında yansıması
  • Çalınmış cookie'nin başka client'ta kullanılmaması

Örneğin cookie içinde şu state imzalı olarak taşınıyor:

{
  "userId": 42,
  "role": "admin",
  "issuedAt": 1785120000,
  "expiresAt": 1785206400
}

Attacker cookie'yi değiştiremese bile geçerli değeri çaldığında expiration'a kadar replay edebilir. Kullanıcının role'ü kaldırıldığında application yalnızca imzalı claim'e güveniyorsa eski admin state yaşamaya devam eder.

Django dokümantasyonu cookie-based session backend için bu ayrımı açıkça belirtir. MAC authenticity ve integrity sağlayabilir fakat freshness sağlamaz. Ayrıca server-side session kaydı bulunmadığı için logout, çalınmış eski cookie'nin yeniden kullanılmasını kendiliğinden engellemeyebilir.

Client-side session kullanılacaksa en az şu kontroller değerlendirilmelidir:

  • Dar absolute lifetime
  • sessionVersion veya securityVersion
  • Server-side revocation record
  • Key rotation ve key identifier
  • Encryption gerekiyorsa authenticated encryption
  • Cookie size limiti
  • Replay-sensitive action için reauthentication
  • Role ve permission gibi volatile state'in her request'te güvenilir kaynaktan alınması

“Stateless” olmak güvenlik state'inin yok olduğu anlamına gelmez. Yalnızca state'in hangi tarafta ve hangi maliyetle yönetildiğini değiştirir.

Hata 24: Remember-me token'ını düşük riskli convenience cookie sanmak

Remember-me özelliği normal browser session kapandıktan sonra kullanıcıyı yeniden authenticated hale getirebilir. Bu nedenle uzun ömürlü authenticator gibi ele alınmalıdır.

Yaygın hatalar:

  • Token aylarca aynı kalır
  • Database'de plaintext tutulur
  • Her kullanımda rotate edilmez
  • Logout yalnızca short-lived session'ı kapatır
  • Password değişikliğinde remember-me token geçerli kalır
  • Device listesinde görünmez
  • MFA gerektiren role için aynı assurance ile kabul edilir
  • Token theft tespit edilemez

Güvenli modelde remember-me token:

  • Random ve yüksek entropy'li üretilir
  • Server-side yalnızca gerekli doğrulama materyaliyle saklanır
  • Kullanımda rotate edilir
  • Device veya token family olarak izlenir
  • Inactivity ve absolute expiration'a sahiptir
  • Password reset, MFA removal ve compromise event'inde revoke edilir
  • High-risk action için yeterli authentication kabul edilmez

Kullanıcı remember-me token ile sessizce session açtığında auth_time ve amr değerleri gerçek password veya MFA authentication'ı gibi işaretlenmemelidir.

Hata 25: Mobile application'da refresh token'ı sıradan local data olarak saklamak

Native mobile uygulamada browser cookie'si yerine access ve refresh token kullanılabilir. Risk yalnızca network interception değildir.

Token şu yollarla açığa çıkabilir:

  • Plain preferences veya local database
  • Device backup
  • Debug log
  • Crash report
  • Clipboard
  • Screenshot
  • Deep link
  • Inter-process communication
  • Rooted veya jailbroken device
  • Malware ve accessibility abuse
  • Test build içindeki verbose telemetry

Refresh token platformun protected keystore capability'siyle korunmalı, application data backup policy'si kontrol edilmeli ve token loglanmamalıdır.

Ancak secure storage token'ı mutlak güvenli yapmaz. Application process token'ı kullanabilmek için belirli noktada erişir. Runtime compromise durumunda theft yine mümkündür.

Bu nedenle mobile lifecycle şunları da içermelidir:

  • Refresh token rotation veya sender constraint
  • Token family replay detection
  • Device registration
  • Remote device revoke
  • Inactivity expiration
  • Absolute lifetime
  • App reinstall ve backup restore behavior
  • Biometric veya device unlock ile local activation
  • High-risk action için server-side step-up

Push notification token, application session değildir. Device notification alabiliyor diye user session'ın geçerli olduğu varsayılmamalıdır.

Hata 26: Credentialed CORS configuration'ını session güvenliğinden ayrı görmek

Browser cookie'yi cross-origin request'e ekleyebilir. JavaScript'in response'u okuyabilmesi CORS policy'ye bağlıdır.

Riskli yaklaşım:

app.use((req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
  res.setHeader("Access-Control-Allow-Credentials", "true");
  next();
});

Bu kod request içindeki her Origin değerini response'a yansıtır. Browser session cookie'yi gönderiyorsa malicious origin authenticated response'u okuyabilir.

Güvenli yaklaşım exact origin allowlist kullanır:

const allowedOrigins = new Set([
  "https://app.example.com",
  "https://admin.example.com"
]);

app.use((req, res, next) => {
  const origin = req.headers.origin;

  if (origin && allowedOrigins.has(origin)) {
    res.setHeader("Access-Control-Allow-Origin", origin);
    res.setHeader("Access-Control-Allow-Credentials", "true");
    res.setHeader("Vary", "Origin");
  }

  next();
});

Allowlist kontrolü:

  • Exact scheme
  • Exact hostname
  • Gerekliyse exact port

üzerinden yapılmalıdır.

Şu kontroller risklidir:

origin.endsWith("example.com")
origin.includes("example.com")
originRegex without escaping
null origin accepted by default

attackerexample.com veya example.com.attacker.tld gibi değerler zayıf suffix ve substring kontrollerini geçebilir.

CORS bir authorization mekanizması değildir. Non-browser client CORS'u uygulamaz. Aynı şekilde CORS'un response okumayı engellemesi CSRF request'inin gönderilemeyeceği anlamına gelmez. Authentication, authorization ve CSRF kontrolleri server-side kalmalıdır.

Hata 27: Farklı workflow'ların aynı session attribute'larını kullanması

Session puzzling, bir workflow'un session'a yazdığı state'in başka bir workflow tarafından farklı anlamla kullanılmasıdır.

Örnek:

Password reset:
session["verified"] = true

Payment approval:
if session["verified"] == true:
    allow_sensitive_action()

Her iki flow kendi içinde mantıklı görünebilir. Ancak generic verified key'i farklı güvenlik anlamlarını birbirine bağlar. Attacker düşük riskli flow'da state'i oluşturup yüksek riskli flow'da kullanabilir.

Benzer riskli attribute'lar:

  • userId
  • verified
  • authorized
  • step
  • returnUrl
  • tenantId
  • isAdmin
  • mfaComplete
  • challengePassed

Güvenli tasarımda state:

  • Workflow'a özel namespace kullanır
  • Purpose ile bağlanır
  • Subject ve target object içerir
  • Dar expiration'a sahiptir
  • Tek kullanımlık olabilir
  • Flow tamamlandığında silinir
  • Başka endpoint tarafından generic boolean olarak yorumlanmaz

Örnek:

{
  "purpose": "payment-beneficiary-addition",
  "subjectId": "user-42",
  "targetId": "beneficiary-draft-918",
  "assuranceLevel": "aal2",
  "verifiedAt": "2026-07-27T14:05:00Z",
  "expiresAt": "2026-07-27T14:10:00Z",
  "nonce": "single-use-reference"
}

State değiştiren multi-step flow yalnızca “önceki sayfa ziyaret edildi mi?” diye kontrol edilmemelidir. Server, her transition'ın doğru predecessor state'ten geldiğini, aynı subject ve object'e ait olduğunu doğrulamalıdır.

Güvenli Express session configuration örneği

Aşağıdaki örnek production için başlangıç baseline'ıdır. Store, proxy ve timeout değerleri deployment'a göre uyarlanmalıdır.

import session from "express-session";

const sessionMiddleware = session({
  name: "__Host-session",

  secret: [
    process.env.SESSION_SECRET_CURRENT,
    process.env.SESSION_SECRET_PREVIOUS
  ],

  store: productionSessionStore,

  resave: false,
  saveUninitialized: false,
  rolling: true,

  cookie: {
    path: "/",
    domain: undefined,
    secure: true,
    httpOnly: true,
    sameSite: "lax",
    maxAge: 15 * 60 * 1000
  }
});

app.use(sessionMiddleware);

Bu configuration'ın açıklaması:

  • __Host-session: Browser-enforced host-only cookie property
  • secure: true: Yalnızca HTTPS
  • httpOnly: true: JavaScript cookie değerini okuyamaz
  • sameSite: "lax": CSRF için defense in depth
  • domain: undefined: Cookie subdomain'lere genişletilmez
  • resave: false: Gereksiz parallel overwrite riskini azaltır
  • saveUninitialized: false: Gereksiz anonymous session oluşturmaz
  • Secret array: Yeni secret ile signing, eski secret ile geçici verification
  • Production store: Default MemoryStore kullanılmaz

Ancak eksik kalanlar vardır:

  • Absolute timeout custom server-side field ile uygulanmalı
  • Login ve privilege change sonrasında regenerate() çağrılmalı
  • CSRF protection ayrıca etkin olmalı
  • Logout store kaydını silmeli
  • Secret rotation planı ve retirement tarihi olmalı
  • Store read/write failure behavior test edilmeli
  • Reverse proxy trust doğru yapılandırılmalı

Güvenli Spring Boot session baseline

application.yml örneği:

server:
  servlet:
    session:
      timeout: 15m
      cookie:
        name: __Host-session
        path: /
        http-only: true
        secure: true
        same-site: strict

Bu ayarlar cookie ve idle timeout için baseline sağlar. Absolute timeout, risk event revocation, session version ve distributed store davranışı ayrıca tasarlanmalıdır.

Spring Security custom authentication yapısında şu alanlar source code review sırasında incelenmelidir:

  • SecurityFilterChain
  • SessionCreationPolicy
  • SecurityContextRepository
  • Authentication success handler
  • Session fixation strategy
  • Logout handler
  • Concurrent session control
  • Remember-me service
  • CSRF configuration
  • OAuth2 login success flow
  • Custom filter order
  • Spring Session repository

Java Spring projelerinde güvenlik incelemesinin yalnızca controller'lardan başlamaması gerektiğini Java Spring projelerinde güvenlik kod incelemesine nereden başlanır? yazımızda ayrıntılı olarak açıklıyoruz.

Oturum güvenliği nasıl test edilir?

Cookie attribute kontrolü yalnızca başlangıçtır.

1. Session inventory çıkarın

  • Hangi cookie ve token'lar authentication taşıyor?
  • Anonymous ve authenticated session ayrı mı?
  • Access, refresh ve IdP session'ları neler?
  • Mobile ve browser aynı lifecycle'ı mı kullanıyor?
  • WebSocket hangi credential ile bağlanıyor?

2. Entropy ve içerik analizi yapın

  • Çok sayıda session ID toplanır
  • Length ve encoding incelenir
  • Sabit prefix veya predictable segment aranır
  • User, time, IP ve server correlation kontrol edilir
  • Duplicate ve collision behavior test edilir

Entropy yalnızca sample'a bakılarak kesin kanıtlanamaz. White box incelemede generator ve source doğrulanmalıdır.

3. Exchange mechanism'lerini test edin

  • Cookie
  • URL parameter
  • POST body
  • Custom header
  • Duplicate cookie
  • Cookie ile URL çakışması
  • Case ve encoding variation

Uygulama yalnızca tasarlanan mechanism'i kabul etmelidir.

4. Rotation matrisi oluşturun

OlayEski session geçerli mi?Yeni ID üretiliyor mu?
LoginHayırEvet
MFA completionPolicy'ye göre hayırEvet
Role elevationHayırEvet
Tenant switchEski context kabul edilmemeliTercihen evet
Password resetDiğer session'lar policy'ye göre revokeEvet
Impersonation start/endHayırEvet

5. Timeout'u server-side doğrulayın

  • Idle timeout
  • Absolute timeout
  • Renewal timeout
  • Background polling etkisi
  • Cookie expiry ile store TTL uyumu
  • Clock skew
  • Replica ve failover
  • Expired session replay

6. Logout matrisi oluşturun

  • Current session
  • Other browser
  • Mobile app
  • Refresh token
  • IdP session
  • WebSocket
  • Remember-me token
  • Cached sensitive page

7. CSRF ve SameSite behavior'ını test edin

  • Cross-site form POST
  • Top-level navigation
  • Sibling subdomain
  • SameSite=None flow
  • Origin ve Referer handling
  • Missing ve malformed CSRF token
  • Login ve logout endpoint'i

8. XSS etkisini ayrı değerlendirin

HttpOnly cookie okunamasa bile authenticated action, response reading ve new credential creation test edilir.

9. Concurrent ve distributed behavior'ı test edin

  • Parallel update
  • Parallel logout ve request
  • Refresh token race
  • Session rotation overlap
  • Store failover
  • Replica lag
  • Node değişimi
  • Clock difference

10. Source code ile root cause'u doğrulayın

Black box test vulnerability'yi gösterebilir. Source code review ise:

  • Session ID nerede üretiliyor?
  • Rotation hangi flow'larda atlanıyor?
  • Timeout client-side mı server-side mı?
  • Revocation hangi store'a yazılıyor?
  • Role claim ne kadar süre cache'leniyor?
  • Logout hangi token family'yi kapatıyor?
  • WebSocket session event'ini alıyor mu?

sorularına cevap verir.

Otomatik SAST, hardcoded secret veya bazı insecure API kullanımlarını bulabilir. Ancak multi-step session lifecycle ve logout davranışını çoğu zaman tam kuramaz. Bu ayrımı Secure code review ile otomatik SAST taraması farkı yazımızda ele alıyoruz.

Oturum güvenliği için acceptance criteria örneği

“Session güvenli olmalı” test edilebilir bir requirement değildir.

Daha iyi acceptance criteria:

SM-01
Authenticated session ID, successful login ve MFA completion sonrasında
değişmelidir. Pre-authentication ID tekrar kullanıldığında protected
endpoint 401 dönmelidir.

SM-02
Session cookie Secure, HttpOnly ve explicit SameSite attribute'larıyla
üretilmelidir. Domain attribute'u bulunmamalı ve __Host- prefix
gereksinimleri karşılanmalıdır.

SM-03
Idle timeout server-side 15 dakika olarak uygulanmalıdır. Background
polling kullanıcı aktivitesi kabul edilmemelidir.

SM-04
Absolute timeout son reauthentication'dan itibaren sekiz saati
geçmemelidir. Continuous activity bu süreyi uzatmamalıdır.

SM-05
Logout sonrasında session ID'nin browser dışından replay edilmesi
protected resource erişimi sağlamamalıdır.

SM-06
Password reset ve MFA removal sonrasında user'a ait bütün refresh token
family'leri ve policy kapsamındaki server-side session'lar revoke
edilmelidir.

SM-07
WebSocket connection, bağlı session revoke edildiğinde en fazla 30 saniye
içinde kapatılmalıdır.

SM-08
Raw session ID, access token ve refresh token application, proxy, APM
ve audit log'larında bulunmamalıdır.

Değerler örnektir. Risk ve use case'e göre belirlenmelidir. Önemli olan başlangıç event'i, beklenen sonuç ve doğrulama yönteminin ölçülebilir olmasıdır.

Geliştirici kontrol listesi

Session creation

  • [ ] Framework'ün güncel session implementation'ı kullanılıyor
  • [ ] Custom ID gerekiyorsa CSPRNG kullanılıyor
  • [ ] En az 128 bit random value hedefleniyor
  • [ ] Session ID anlamsız ve opaque
  • [ ] Anonymous ve authenticated session ayrımı açık

Cookie

  • [ ] Secure etkin
  • [ ] HttpOnly etkin
  • [ ] SameSite explicit
  • [ ] Domain gereksiz yere tanımlanmamış
  • [ ] Path doğru
  • [ ] Uygunsa __Host- prefix kullanılıyor
  • [ ] Bütün session boyunca HTTPS ve HSTS uygulanıyor

Lifecycle

  • [ ] Login sonrasında rotation
  • [ ] MFA completion sonrasında rotation
  • [ ] Privilege change sonrasında rotation veya revocation
  • [ ] Idle timeout server-side
  • [ ] Absolute timeout server-side
  • [ ] Risk event için reauthentication
  • [ ] Concurrent session policy

Revocation

  • [ ] Logout server-side invalidate ediyor
  • [ ] Cookie doğru scope ile expire ediliyor
  • [ ] Password reset session policy'sini tetikliyor
  • [ ] Refresh token family revoke edilebiliyor
  • [ ] “Logout all devices” gerçekten bütün state'i kapsıyor
  • [ ] Disabled account'ın mevcut session'ı çalışmıyor

Distributed store

  • [ ] Store production için uygun
  • [ ] TTL server-side
  • [ ] Delete ve rotation consistency tanımlı
  • [ ] Parallel update davranışı test edilmiş
  • [ ] Failover ve replica lag değerlendirilmiş
  • [ ] Session data minimize edilmiş
  • [ ] Store access'i authentication ve encryption ile korunuyor

Browser ve client

  • [ ] Session secret URL'de değil
  • [ ] Raw token loglanmıyor
  • [ ] Browser session secret localStorage içinde değil
  • [ ] Sensitive response cache policy'si tanımlı
  • [ ] CSRF protection etkin
  • [ ] XSS ayrı savunma katmanlarıyla ele alınıyor

Long-lived channel

  • [ ] WebSocket handshake authentication
  • [ ] Origin validation
  • [ ] Message-level authorization
  • [ ] Session revocation event'i connection'a ulaşıyor
  • [ ] Idle ve absolute lifetime var
  • [ ] Logout connection'ı kapatıyor

Monitoring

  • [ ] Creation, rotation ve revocation event'leri loglanıyor
  • [ ] Raw secret yerine güvenli correlation reference kullanılıyor
  • [ ] Token replay tespit ediliyor
  • [ ] Anormal concurrent session davranışı izleniyor
  • [ ] Risk signal'ları privacy review'den geçmiş

Sonuç: Session bir cookie değil, uçtan uca güven ilişkisidir

Oturum güvenliği çoğu projede login tamamlandıktan sonra framework'e bırakılan teknik ayrıntı gibi görülür. Oysa session, authentication sonucunu authorization kararlarına taşıyan temel güvenlik boundary'sidir.

Güvenli bir session tasarımında:

  • Session secret CSPRNG ile üretilir
  • Client'a anlamsız ve minimum bilgi taşıyan reference verilir
  • Cookie scope mümkün olduğunca dar tutulur
  • Bütün lifecycle HTTPS ile korunur
  • Login ve privilege change sonrasında ID rotate edilir
  • Idle ve absolute timeout server-side uygulanır
  • Logout gerçek revocation üretir
  • Password reset ve MFA değişikliği session policy'sini tetikler
  • CSRF ve XSS ayrı riskler olarak ele alınır
  • JWT ve refresh token replay ile revocation modeliyle tasarlanır
  • Distributed store consistency bir security requirement kabul edilir
  • WebSocket connection HTTP session'dan kopuk yaşamaz
  • Raw token loglanmaz
  • Session lifecycle ölçülebilir test case'lere dönüştürülür

En sık yapılan hata tek bir attribute'u güvenlik sonucu sanmaktır. HttpOnly vardır fakat XSS session üzerinden işlem yapabilir. SameSite vardır fakat CSRF boundary eksiktir. JWT imzalıdır fakat logout token'ı iptal etmez. Timeout ekranda görünür fakat backend eski ID'yi kabul eder.

Doğru soru “Cookie üzerinde hangi flag'ler var?” değildir:

Not

Bu authentication sonucu hangi client'a, hangi süreyle, hangi privilege ve tenant context'iyle bağlandı? Hangi olaylarda değişiyor, nasıl iptal ediliyor ve eski state'in yeniden kullanılmadığını hangi test kanıtlıyor?

SECNODEX, web application ve API sızma testlerinde session management'ı yalnızca automated cookie kontrolü olarak değerlendirmez. Login, MFA, SSO, privilege transition, CSRF, timeout, logout, refresh token, concurrent request ve WebSocket lifecycle'ı farklı account ve role'lerle test edilir. OSCP ve OSWE sertifikalarına sahip uzman kadromuz, runtime behavior'ı source code içindeki root cause ile birleştirerek reproducible evidence ve uygulanabilir remediation üretir.

Uygulamanızdaki oturum güvenliğini uçtan uca değerlendirmek için SECNODEX Sızma Testi hizmetini, implementation kaynaklı lifecycle problemlerini incelemek için Kaynak Kod Analizi ve Secure Code Review hizmetimizi inceleyebilir veya bizimle iletişime geçebilirsiniz.

Kaynaklar

#Oturum Güvenliği#Session Management#Session Fixation#Session Hijacking#Cookie Security#JWT#CSRF#Web Application Security

Sık sorulan sorular

Oturum yönetimi nedir?

Oturum yönetimi, authentication sonucunun sonraki request'lere güvenli biçimde bağlanması, session state'in korunması, yenilenmesi, zaman aşımına uğratılması ve iptal edilmesi sürecidir.

Oturum güvenliği neden önemlidir?

Geçerli session secret çoğu application'da kullanıcı adına işlem yapmak için yeterlidir. Session ele geçirildiğinde saldırgan password veya MFA adımını yeniden geçmeden authenticated access elde edebilir.

Session ID kaç bit olmalı?

NIST session binding secret için en az 64 bit şartı verir. OWASP custom session ID için CSPRNG ile en az 128 bit önerir. Modern uygulamalarda 128 veya 256 bit random value kullanmak yaygındır. Asıl ölçüt encoded string uzunluğu değil effective entropy'dir.

Secure ve HttpOnly yeterli mi?

Hayır. Bu attribute'lar transport ve JavaScript erişimi riskini azaltır. Session fixation, timeout, logout, revocation, CSRF, privilege change ve server-side store sorunlarını çözmez.

SameSite CSRF'i tamamen engeller mi?

Hayır. Güçlü defense in depth sağlar fakat sibling subdomain, login CSRF, client-side CSRF, cross-site SSO ve SameSite=None use case'leri nedeniyle CSRF protection ayrıca tasarlanmalıdır.

Session fixation nedir?

Attacker'ın bildiği veya seçtiği session ID'nin victim login olduktan sonra da korunmasıdır. Login ve privilege transition sonrasında session ID rotate edilerek önlenir.

Logout sırasında yalnızca cookie silmek yeterli mi?

Hayır. Server-side session veya token grant de invalid hale getirilmelidir. Aksi halde önceden çalınan değer replay edilebilir.

Idle timeout ile absolute timeout farkı nedir?

Idle timeout kullanıcı belirli süre etkin değilse session'ı sonlandırır. Absolute timeout kullanıcı aktif olsa bile authentication veya reauthentication sonrasında belirli sürede session'ı kapatır.

JWT kullanmak session management ihtiyacını ortadan kaldırır mı?

Hayır. Token issuance, storage, expiration, refresh, rotation, replay ve revocation hâlâ yönetilmelidir. Stateless verification merkezi logout ve yetki değişikliğini zorlaştırabilir.

Access token nerede saklanmalı?

Tek bir evrensel cevap yoktur. Browser SPA, BFF, native mobile ve confidential client modelleri farklıdır. Browser'da session secret'ın localStorage içinde tutulması XSS riskini büyütür. Architecture threat model'e göre seçilmelidir.

Password değişince bütün session'lar kapanmalı mı?

Risk ve policy'ye bağlıdır. Password reset, account recovery veya compromise şüphesinde global revocation güçlü yaklaşımdır. Normal password change flow'unda current reauthenticated session korunup diğerleri sonlandırılabilir.

IP address'e session binding yapılmalı mı?

IP tek başına katı binding için güvenilir değildir. Mobile network, VPN ve proxy değişimleri false positive oluşturur. Risk signal'ı olarak kullanılıp step-up veya revocation kararına katkı sağlaması daha dengelidir.

WebSocket logout sonrasında açık kalabilir mi?

Güvenli tasarımda kalmamalıdır. Connection session ile eşleştirilmeli, expiration ve revocation izlenmeli, logout sonrasında kapatılmalı ve her message için authorization uygulanmalıdır.

Session cookie'nin adı neden `__Host-` ile başlamalı?

Bu prefix browser'a cookie'nin Secure, host-only ve Path=/ özelliklerini enforce ettirir. Subdomain tarafından daha geniş scope'lu cookie set edilmesi riskini azaltır.

Oturum güvenliği SAST ile bulunabilir mi?

Bazı insecure API kullanımları, hardcoded secret ve configuration problemleri bulunabilir. Ancak rotation, distributed revocation, logout, timeout ve cross-channel lifecycle çoğunlukla manual secure code review ile runtime testin birlikte uygulanmasını gerektirir.

Oturum güvenliği sızma testinde nasıl doğrulanır?

Session fixation, ID rotation, cookie attribute, timeout, logout replay, CSRF, concurrent session, token revocation, federation ve WebSocket behavior'ı farklı kullanıcı ve client context'leriyle test edilir. Coverage planını Web uygulaması sızma testinde kapsam nasıl belirlenir? rehberimizle birlikte değerlendirebilirsiniz.

Siber güvenlik çalışmanızı
SECNODEX ile planlayın

İhtiyacınız tek bir uygulamanın testinden kurum genelinde bir Red Team çalışmasına kadar uzanabilir. Ekibimiz scope’u, kritik varlıkları ve beklenen çıktıları sizinle netleştirir. Ardından iş hedefinize ve risk önceliklerinize uygun, sınırları belirlenmiş bir çalışma planı sunar.