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.
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 auditBu 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:
| Katman | Soru |
|---|---|
| Authentication | Bu subject kim? |
| Session management | Bu kimlik sonucu sonraki request'lere nasıl güvenli bağlanıyor? |
| Authorization | Bu 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-valueBackend 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:
CookieSet-CookieAuthorization- 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=LaxUse 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ırLax: Top-level safe navigation için daha uyumludurNone: Cross-site gönderime izin verir veSecuregerektirir
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; HttpOnlyMarketing, 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:
SecureolmalıdırDomainiçermemelidirPath=/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 IDRotation ş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 contextSpring 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:
- 1Eski session role bilgisini cache'lediği için yeni yetki beklenmedik anda geçerli olmaz
- 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,amrve 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: 18Password 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_timeoutHer 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_timeoutrolling: 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ı
401veya 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:
- 1Server-side session invalidate edilir
- 2Session cookie doğru attribute'larla expire edilir
- 3İlgili access ve refresh token policy'ye göre revoke edilir
- 4Long-lived connection'lar kapatılır
- 5Sensitive cached response'lar için uygun cache policy uygulanır
- 6Audit event üretilir
- 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,nbfvalidation- 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 edilirSadece 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:
| State | Owner | Storage | Timeout | Rotation | Revocation |
|---|---|---|---|---|---|
| Browser cookie | Application | Browser | Idle + absolute | Login ve elevation | Cookie expire + server invalidate |
| Server session | Application | Redis veya database | Server-enforced | Session ID | Store delete |
| Access token | Authorization Server | Client memory/BFF | Short | Yeni issuance | Introspection veya short expiry |
| Refresh token | Authorization Server | Protected client store | Inactivity + absolute | Her kullanım | Family revoke |
| IdP session | Identity Provider | Browser + IdP | IdP policy | Reauthentication | IdP logout |
| WebSocket state | Realtime service | Process/distributed map | Connection + auth expiry | Protocol-specific | Connection 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:
- 1Kullanıcı application'da logout olur
- 2Local RP session kapanır
- 3IdP session açık kalır
- 4Kullanıcı login'e basar
- 5IdP mevcut session ile yeni authorization response üretir
- 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_timemax_ageacramr- 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:
- 1Request A session'a
mfaVerified=trueyazar - 2Request B eski session snapshot'ını yükler
- 3Request A store'a kaydeder
- 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önemezHata 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 revokeKarar 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: RequiredHata 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:
Originallowlist 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-cacheno-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
sessionVersionveyasecurityVersion- 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 defaultattackerexample.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:
userIdverifiedauthorizedstepreturnUrltenantIdisAdminmfaCompletechallengePassed
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 propertysecure: true: Yalnızca HTTPShttpOnly: true: JavaScript cookie değerini okuyamazsameSite: "lax": CSRF için defense in depthdomain: undefined: Cookie subdomain'lere genişletilmezresave: false: Gereksiz parallel overwrite riskini azaltırsaveUninitialized: false: Gereksiz anonymous session oluşturmaz- Secret array: Yeni secret ile signing, eski secret ile geçici verification
- Production store: Default
MemoryStorekullanı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: strictBu 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:
SecurityFilterChainSessionCreationPolicySecurityContextRepository- 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
| Olay | Eski session geçerli mi? | Yeni ID üretiliyor mu? |
|---|---|---|
| Login | Hayır | Evet |
| MFA completion | Policy'ye göre hayır | Evet |
| Role elevation | Hayır | Evet |
| Tenant switch | Eski context kabul edilmemeli | Tercihen evet |
| Password reset | Diğer session'lar policy'ye göre revoke | Evet |
| Impersonation start/end | Hayır | Evet |
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=Noneflow- 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
- [ ]
Secureetkin - [ ]
HttpOnlyetkin - [ ]
SameSiteexplicit - [ ]
Domaingereksiz yere tanımlanmamış - [ ]
Pathdoğ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
localStorageiç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
- OWASP Session Management Cheat Sheet
- OWASP Cross-Site Request Forgery Prevention Cheat Sheet
- OWASP WebSocket Security Cheat Sheet
- OWASP Web Security Testing Guide – Session Management Testing
- OWASP WSTG – Testing for Session Fixation
- OWASP WSTG – Testing for Cookies Attributes
- OWASP Application Security Verification Standard
- NIST SP 800-63B-4 – Session Management
- RFC 6265 – HTTP State Management Mechanism
- RFC 9700 – Best Current Practice for OAuth 2.0 Security
- RFC 7009 – OAuth 2.0 Token Revocation
- OpenID Connect Back-Channel Logout 1.0
- Spring Security – Authentication Persistence and Session Management
- Express – Session Middleware
- Django – How to Use Sessions
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.
Okumaya devam et
Web Uygulama Güvenliği
IDOR Açığı Neden Hâlâ Bu Kadar Yaygın?
IDOR, identifier tahmin edilebildiği için değil, server her object access sırasında doğru authorization kararını veremediği için oluşur. Yaygınlığın mimari ve süreç nedenlerini inceliyoruz.
Yazıyı okuWeb Uygulama Güvenliği
Güvenli Dosya Yükleme için Geliştirici Kontrol Listesi
Güvenli dosya yükleme yalnızca extension veya MIME kontrolü değildir. Request limitinden quarantine alanına, parser isolation'dan malware scanning ve güvenli download'a kadar bütün file lifecycle korunmalıdır.
Yazıyı oku