Uygulama güvenliği: Spring Security, JWT, OAuth2
Authentication/authorization, Spring Security mimarisi ve 6+/7 yapılandırması, JWT ve token yaşam döngüsü, OAuth2/OIDC akışları, CORS/CSRF, şifre ve secret saklama, OWASP riskleri ve bankacılığa özgü önlemler üzerine başlangıçtan ileri seviyeye mülakat soruları.
Konu özeti
Authentication, authorization ve 401/403
Authentication (kimlik doğrulama) "sen kimsin?" sorusunu cevaplar: şifre, token, sertifika ya da biyometriyle kimliğin kanıtlanması. Authorization (yetkilendirme) "bunu yapmaya iznin var mı?" sorusudur: rol, scope ve kaynak sahipliği. Sıra hep aynıdır: önce kimlik, sonra yetki. HTTP karşılıkları da farklıdır: kimlik yoksa ya da geçersizse 401 Unauthorized (adı yanıltıcıdır, anlamı "unauthenticated"dır; WWW-Authenticate header'ıyla döner), kimlik belli ama yetki yoksa 403 Forbidden. Başkasına ait kaynakta varlığı gizlemek için 404 dönmek de meşru bir tercihtir (bkz. Kitap Bölüm 18).
Session mı, token mı?
Klasik web uygulamasında sunucu login sonrası bir session açar ve istemciye sadece rastgele bir session id'si (JSESSIONID cookie'si) verir; kullanıcının kim olduğu sunucuda (bellek, Redis, Spring Session) durur. İptal anlıktır (session'ı sil), ama yatay ölçeklemede ortak bir session deposu gerekir ve cookie tarayıcı tarafından otomatik gönderildiği için CSRF koruması şarttır. Token tabanlı yaklaşımda (çoğunlukla JWT) kimlik bilgisi imzalı token'ın içindedir; istemci her istekte Authorization: Bearer <token> gönderir, sunucu imzayı ve süreyi doğrulayıp veritabanına gitmeden kullanıcıyı tanır. Bedeli iptal zorluğu ve token boyutudur. "JWT modern, session eski" diye bir kural yoktur: tek bir tarayıcı uygulaması için session hâlâ çok iyi bir seçimdir; çok servisli ve çok istemcili sistemlerde token öne çıkar.
Spring Security mimarisi
Spring Security servlet tarafında bir filter zinciridir ve controller'dan önce çalışır. Servlet container'a kayıtlı DelegatingFilterProxy, işi bir Spring bean'i olan FilterChainProxy'ye devreder; o da isteğe uyan ilk SecurityFilterChain'i seçip içindeki filter'ları sırayla çalıştırır. Kabaca sıra şöyledir: context'i yükleyen filter, CORS, CSRF, logout, kimlik doğrulama filter'ları (form login, HTTP Basic, BearerTokenAuthenticationFilter), anonymous, ExceptionTranslationFilter ve en sonda AuthorizationFilter.
Kimlik doğrulama filter'ı istekten bir Authentication nesnesi üretip AuthenticationManager'a verir. Varsayılan uygulama ProviderManager, işi bir AuthenticationProvider listesine devreder. Kullanıcı adı/şifre için DaoAuthenticationProvider, kullanıcıyı UserDetailsService ile yükler ve şifreyi PasswordEncoder ile karşılaştırır. Başarılı sonuç SecurityContext'e konur; SecurityContextHolder bunu varsayılan olarak ThreadLocal'da tutar, FilterChainProxy da istek bitince temizler. Hatalar ExceptionTranslationFilter'da HTTP'ye çevrilir: AuthenticationException (ya da anonim kullanıcının yetki reddi) AuthenticationEntryPoint ile 401'e, kimliği doğrulanmış kullanıcının AccessDeniedException'ı AccessDeniedHandler ile 403'e.
Spring Security 6+ ve 7 yapılandırması
WebSecurityConfigurerAdapter 5.7'de deprecate edildi, 6.0'da kaldırıldı; yerine SecurityFilterChain tipinde bir bean tanımlanır. 7.0'da (Kasım 2025; Spring Boot 4.0 bu nesli kullanır) and() zinciri ve authorizeRequests kaldırıldı; lambda DSL ve authorizeHttpRequests tek yoldur. Spring Boot 3.x Spring Security 6.x ile gelir. Aşağıdaki stil iki nesilde de çalışır:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
class SecurityConfig {
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/public/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/payments/**").hasAuthority("SCOPE_payments.write")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(csrf -> csrf.disable()); // safe only because credentials travel in a header, not a cookie
return http.build();
}
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder(); // bcrypt by default
}
}
Kurallar yazıldığı sırayla değerlendirilir ve ilk eşleşen kazanır; bu yüzden özelden genele yazılır ve sonda anyRequest() olur. Method security için @EnableMethodSecurity (deprecate edilmiş @EnableGlobalMethodSecurity'nin yerine) @PreAuthorize ve @PostAuthorize'ı açar. Bu kontroller Spring AOP proxy'siyle çalışır: aynı sınıf içinden this.method() çağrısı proxy'yi atladığı için kontrol çalışmaz (@Transactional ile aynı tuzak). hasRole('ADMIN') aslında ROLE_ADMIN yetkisini arar; OAuth2 resource server ise scope'ları SCOPE_ önekiyle yetkiye çevirir.
Şifre saklama
Şifre asla düz metin ya da geri çözülebilir şekilde (encryption) saklanmaz; tek yönlü, kasıtlı olarak yavaş ve salt'lı bir hash ile saklanır. MD5 ve SHA-256 gibi hızlı hash'ler şifre için uygun değildir, çünkü GPU ile saniyede milyarlarca deneme yapılabilir. OWASP Password Storage Cheat Sheet'in sırası: Argon2id, scrypt, bcrypt (eski sistemler için), FIPS gerekiyorsa PBKDF2. Salt her kullanıcı için rastgeledir ve hash'le birlikte saklanır; aynı şifrelerin aynı hash'i üretmesini ve hazır tabloları (rainbow table) engeller. Spring'in DelegatingPasswordEncoder'ı hash'i {bcrypt}$2a$10$... biçiminde önekle saklar, böylece algoritma ileride değiştirilebilir. BCryptPasswordEncoder'ın varsayılan strength'i 10'dur; Spring dokümanı maliyetin, doğrulama sistemde yaklaşık bir saniye sürecek şekilde ayarlanmasını önerir. bcrypt'in 72 byte girdi sınırı da unutulmamalıdır.
JWT
JSON Web Token (RFC 7519) üç Base64URL parçadan oluşur: header.payload.signature. Header algoritmayı (alg, ör. RS256) ve anahtar kimliğini (kid), payload claim'leri (iss, sub, aud, exp, iat, nbf, jti, scope/rol) taşır; imza ilk iki parçanın üzerinden atılır. En yaygın yanlış: yaygın kullanılan JWT (JWS) şifreli değildir, imzalıdır. Payload'u herkes okur; imza sadece içeriğin değişmediğini ve kimin ürettiğini kanıtlar. Gizlilik gerekiyorsa JWE vardır, ama çoğu zaman doğru çözüm token'a hassas veri koymamaktır.
Doğrulamada imza, sunucunun belirlediği algoritma listesi, exp/nbf, iss ve aud kontrol edilir. İki klasik tuzak: alg: none ile imzasız token kabul eden kütüphaneler ve algoritma karışıklığı (RS256 sisteminde token'ı HS256'ya çevirip RSA public key'ini HMAC secret'ı olarak kullandırmak). RFC 8725 (JWT BCP) bu yüzden kabul edilen algoritmaların çağıran tarafından belirlenmesini şart koşar. İptal JWT'nin doğal zayıflığıdır: imza geçerli ve süre dolmamışsa token geçerlidir. Bu yüzden kısa ömürlü access token (ör. 5-15 dakika) ve sunucuda izlenen, her kullanımda yenilenen (rotation) refresh token birlikte kullanılır; anlık iptal için jti denylist'i ya da kullanıcı bazlı token sürümü gibi durum tutan ek bir mekanizma gerekir.
OAuth2 ve OpenID Connect
OAuth 2.0 (RFC 6749) bir yetki devri (delegated authorization) çerçevesidir: kullanıcı şifresini paylaşmadan bir uygulamaya kendi adına sınırlı erişim verir. Dört rol vardır: resource owner (kullanıcı), client (uygulama), authorization server (token veren; Keycloak, Okta, Spring Authorization Server) ve resource server (token'ı kabul eden API; senin Spring Boot servisin). OAuth2 bir protokoldür, JWT ise bir token formatıdır; OAuth2 access token'ı JWT olabilir ya da opaque olabilir.
Önerilen akışlar: kullanıcı varsa authorization code + PKCE (RFC 7636), makineler arası client credentials. RFC 9700 (OAuth 2.0 Security BCP, Ocak 2025) implicit grant'in kullanılmamasını (SHOULD NOT), resource owner password credentials grant'in ise hiç kullanılmamasını (MUST NOT) söyler; public client'lar için PKCE zorunludur. Hâlâ taslak olan OAuth 2.1 bu iki akışı tamamen çıkarır ve PKCE'yi tüm authorization code akışlarına zorunlu kılar; taslak olduğu için ona standart gibi değil, yön gösteren bir derleme gibi atıf yapmak doğrudur.
OAuth2 tek başına bir kimlik doğrulama protokolü değildir. OpenID Connect üzerine kimlik katmanı ekler: openid scope'u, userinfo endpoint'i ve ID token (client'a kullanıcının kim olduğunu söyleyen JWT; aud değeri client id'sidir). ID token API çağırmak için değil, access token API için kullanılır. Spring Boot'ta bir API'yi resource server yapmak için starter eklenip issuer-uri verilir; Spring ilk JWT'li istekte issuer'ın metadata'sından jwks_uri'yi bulur, public key'leri çeker, exp, nbf ve iss'i varsayılan olarak doğrular; aud için audiences eklenir:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.com/realms/bank
audiences: payments-api
Starter adı Boot 3.x'te spring-boot-starter-oauth2-resource-server; Boot 4.0'da spring-boot-starter-security-oauth2-resource-server olarak yeniden adlandırıldı (eski ad geçiş için deprecated tutuluyor).
CORS ve CSRF
İkisi sık karıştırılır. CORS, tarayıcının farklı origin'den gelen JavaScript'in cevabı okumasına izin verip vermeyeceğini belirleyen kontrollü bir gevşetmedir; sunucuyu korumaz, curl'ü durdurmaz (bkz. Kitap Bölüm 18). CSRF bir saldırıdır: kötü site, kullanıcının tarayıcısına otomatik gönderilen cookie ile hedef siteye istenmeyen bir işlem yaptırır. Koruma CSRF token, SameSite cookie ve durum değiştiren işlemlerin GET ile yapılmamasıdır. Spring Security CSRF korumasını varsayılan açık getirir. REST API'de kapatmak, kimlik bilgisi otomatik gönderilmiyorsa (bearer token header'ı, tarayıcı dışı istemciler) güvenlidir; token cookie'de taşınıyorsa API "stateless" olsa bile CSRF yeniden gündemdedir.
OWASP listeleri
OWASP Top 10, web uygulaması riskleri için en bilinen farkındalık listesidir. 2021 sürümü: A01 Broken Access Control, A02 Cryptographic Failures, A03 Injection, A04 Insecure Design, A05 Security Misconfiguration, A06 Vulnerable and Outdated Components, A07 Identification and Authentication Failures, A08 Software and Data Integrity Failures, A09 Security Logging and Monitoring Failures, A10 Server-Side Request Forgery. 2025 sürümü Kasım 2025'te önce release candidate olarak duyuruldu; owasp.org bugün onu güncel liste olarak sunuyor. Broken Access Control birinci kaldı, Security Misconfiguration ikinciye çıktı, A03 Software Supply Chain Failures ve A10 Mishandling of Exceptional Conditions eklendi, SSRF A01'in içine alındı. Mülakatta hangi sürümden bahsettiğini söylemen yeterlidir. API'ler için ayrıca OWASP API Security Top 10 (2023) vardır; birinci maddesi BOLA, yani nesne düzeyinde yetki kontrolünün eksikliğidir (IDOR).
Sık görülen açıklar
- SQL injection: Kullanıcı girdisinin sorgu metnine eklenmesi. Çözüm parametreli sorgudur (
PreparedStatement, JPQL'de:param). JPA kullanmak tek başına korumaz: JPQL ya da native sorguyu string birleştirerek kurmak aynı açığı yaratır.ORDER BYkolonu gibi parametreleştirilemeyen yerlerde beyaz liste kullanılır. - XSS: Girdinin başka bir kullanıcının tarayıcısında script olarak çalışması. Çözüm bağlama göre output encoding, Content-Security-Policy ve token'ı JavaScript'in okuyamayacağı yerde tutmaktır.
- SSRF: Sunucunun kullanıcının verdiği URL'ye istek atması (webhook, "URL'den içe aktar"). Saldırgan iç ağa ya da bulut metadata servisine (
169.254.169.254) eriştirir. Çözüm hedef allowlist'i, DNS çözümlendikten sonra iç IP aralıklarının engellenmesi, yönlendirmelerin kapatılması ve ağ seviyesinde egress kısıtıdır. - Rate limiting: Login, OTP ve kupon gibi tahmin edilebilir uç noktalarda kaba kuvvete karşı; reddedilen isteğe 429 ve
Retry-Afterdönülür.
Taşıma, secret'lar ve loglar
HTTPS (TLS) üç şey sağlar: gizlilik, bütünlük ve sunucu kimliğinin sertifikayla doğrulanması. Bearer token TLS olmadan anlamsızdır, çünkü token'ı ele geçiren onun sahibi olur. Secret'lar (DB şifresi, imza anahtarı, API key) kodda ve git'te durmaz; ortam değişkeni, Kubernetes Secret (Base64 şifreleme değildir) ya da tercihen Vault, AWS Secrets Manager gibi bir secret yöneticisinde tutulur, döndürülür ve sızıntıda önce iptal edilir. Loglara şifre, token, Authorization header'ı, kart numarası ve gereksiz kişisel veri yazılmaz; gerekiyorsa maskelenir (KVKK, PCI DSS).
Bankacılıkta ek katmanlar
Finans API'lerinde genellikle şunlar eklenir: servisler ve kurumlar arasında mTLS (iki taraf da sertifikayla kimliğini kanıtlar; RFC 8705 access token'ı istemci sertifikasına bağlamayı tanımlar), isteğin bütünlüğü ve inkâr edilemezliği için imzalı istekler (JWS ya da HTTP Message Signatures), kritik işlemlerde adım yükseltmeli doğrulama (step-up, OTP), kim-ne-zaman-nereden-sonuç bilgisini değiştirilemez biçimde tutan audit log ve FAPI gibi finansa özel OAuth profilleri. Mülakatta bu kısım derinlemesine değil, "bunların farkında mı" diye yoklanır.
Sorular (32)
401 ile 403 arasındaki fark nedir? Spring Security hangisini ne zaman döner?
Şifreler veritabanında nasıl saklanır?
Hash, encryption ve encoding arasındaki fark nedir?
JWT nedir ve nasıl çalışır?
JWT şifreli midir? Payload'a ne koyarsın, ne koymazsın?
Token süresi (expiration) nasıl çalışır?
Session tabanlı kimlik doğrulama ile JWT arasındaki fark nedir? Hangisini seçersin?
CORS nedir ve CSRF'ten farkı nedir?
SQL injection nedir ve JPA kullanırken nasıl önlenir?
HTTPS ne sağlar? Kurum içi ağda da gerekli mi?
Secret'ları (DB şifresi, API key, imza anahtarı) nerede tutarsın?
OWASP Top 10'dan üç örnek verir misin?
Spring Security'de bir HTTP isteği hangi aşamalardan geçer?
SecurityContext ve SecurityContextHolder nedir? Async kodda ne olur?
@PreAuthorize nasıl çalışır? Neden bazen hiç çalışmaz?
OAuth ile JWT arasındaki fark nedir?
Refresh token akışı nasıl işler?
JWT'yi istemcide nerede saklarsın? localStorage mı, HttpOnly cookie mi?
REST API'de CSRF koruması gerekir mi? Ne zaman kapatırsın?
OAuth2 bir authentication protokolü müdür? OpenID Connect ne ekler?
IDOR/BOLA nedir? Bir örnek ver, nasıl önlersin?
XSS nedir? Backend geliştirici olarak ne yaparsın?
Loglarda hassas veri ve PII nasıl ele alınır?
Login endpoint'ini brute force ve credential stuffing'e karşı nasıl korursun?
JWT'ler geçersiz kılınabilir mi?
Authorization code + PKCE akışını anlatır mısın? Implicit ve password grant neden önerilmiyor?
Servisler arası kimlik doğrulamayı nasıl kurarsın?
JWT doğrulamada algoritma tuzakları nelerdir? Spring Boot resource server'ı güvenli nasıl yapılandırırsın?
API gateway'de kimlik doğrulama ve güvenlik nasıl kurgulanır?
SSRF nedir, nasıl önlenir?
Güvenli bir login endpoint'i tasarlar mısın?
Çoktan seçmeli (21)
Kullanıcının JWT'si geçerli ve süresi dolmamış, ancak ADMIN yetkisi gerektiren bir endpoint'e USER yetkisiyle istek atıyor. API hangi status kodunu dönmelidir?
Tek doğru cevap var.
Kullanıcı şifrelerini veritabanında saklamak için hangisi doğrudur?
Tek doğru cevap var.
İmzalı bir JWT'nin (JWS) payload'u hakkında hangisi doğrudur?
Tek doğru cevap var.
Aşağıdakilerden hangisi bir authorization (yetkilendirme) kontrolüdür?
Tek doğru cevap var.
Spring Data JPA kullanan bir projede hangi yaklaşım SQL/JPQL injection'a açıktır?
Tek doğru cevap var.
CORS hakkında hangisi doğrudur?
Tek doğru cevap var.
Production veritabanı şifresi için en uygun yaklaşım hangisidir?
Tek doğru cevap var.
HTTPS (TLS) aşağıdakilerden hangilerini sağlar? (Birden fazla doğru var)
Birden fazla doğru olabilir.
OWASP Top 10 2021 listesinde birinci sıradaki kategori hangisidir?
Tek doğru cevap var.
Kullanıcının yetkileri arasında öneksiz ADMIN var. @PreAuthorize("hasRole('ADMIN')") ile korunan metot çağrıldığında ne olur?
Tek doğru cevap var.
AccountService içindeki korumasız closeFromBatch() metodu, aynı sınıftaki @PreAuthorize("hasRole('ADMIN')") ile korunan close() metodunu doğrudan (this üzerinden) çağırıyor. Ne olur?
Tek doğru cevap var.
GET /api/v1/accounts/{id} endpoint'inde IDOR'u önlemenin en doğru yolu hangisidir?
Tek doğru cevap var.
Hangi durumda bir REST API için Spring Security CSRF korumasını kapatmak güvenlidir?
Tek doğru cevap var.
OAuth 2.0 ile JWT arasındaki ilişki için hangisi doğrudur?
Tek doğru cevap var.
Refresh token akışı için hangisi doğrudur?
Tek doğru cevap var.
OpenID Connect'teki ID token'ın doğru kullanımı hangisidir?
Tek doğru cevap var.
RS256 ile imzalanan token'ları doğrulayan bir API'de saldırgan header'ı alg: HS256 yapıp imzayı sunucunun public key'ini HMAC secret'ı olarak kullanarak üretiyor. En doğru savunma hangisidir?
Tek doğru cevap var.
PKCE (RFC 7636) authorization code akışında hangi saldırıya karşı koruma sağlar?
Tek doğru cevap var.
Müşterinin hesabı ele geçirildi ve tüm token'larının hemen geçersiz olması gerekiyor. Aşağıdakilerden hangileri buna yardım eder? (Birden fazla doğru var)
Birden fazla doğru olabilir.
API gateway arkasındaki servisler için hangi tasarım en güvenlidir?
Tek doğru cevap var.
Kullanıcının verdiği URL'den resim indiren bir özellikte SSRF'e karşı en etkili önlem hangisidir?
Tek doğru cevap var.
Kaynaklar
- Spring Security: Servlet Architecture:
DelegatingFilterProxy,FilterChainProxy, ilk eşleşenSecurityFilterChain, filter sırası veExceptionTranslationFilter'ın 401/403 davranışı. - Spring Security: Servlet Authentication Architecture:
SecurityContextHolder'ınThreadLocalstratejileri,ProviderManagerveAuthenticationProvider,createEmptyContext()önerisi. - Spring Security: Persisting Authentication:
SecurityContextHolderFilter'ın context'i sadece yüklemesi ve açık kaydetme. - Spring Security: Method Security:
@EnableMethodSecurity, AOP proxy tabanlı çalışma, parametre adlarının çözülmesi veAspectJmodu. - Spring Security: Password Storage:
DelegatingPasswordEncoderbiçimi, bcrypt varsayılan strength 10 ve yaklaşık bir saniye önerisi. - Spring Security: CSRF (Servlet): CSRF'in varsayılan açık olması, tarayıcı trafiği olmayan backend'lerde kapatma ve
csrf.spa(). - Spring Security: OAuth 2.0 Resource Server JWT:
issuer-uriile keşif, varsayılanexp/nbf/issdoğrulaması,audiences,SCOPE_öneki. - What's New in Spring Security 7.0:
and()veauthorizeRequests'in kaldırılması, password grant desteğinin kaldırılması,csrf.spa(). - Spring Security 6.5: Preparing for 7 (Configuration): Lambda DSL'in 7'de zorunlu olması.
- What's New in Spring Security 6.0:
WebSecurityConfigurerAdapter'ın kaldırılması veSecurityFilterChainbean'i. - Spring Security Releases (17 Kasım 2025) ve Spring Boot 4.0.0 available now: Spring Security 7.0.0 ve Spring Boot 4.0.0 çıkış tarihleri.
- RestClient Support for OAuth2 in Spring Security 6.4:
RestClientile client credentials token'larının otomatik eklenmesi. - OpenRewrite: Rename Spring Boot 4.0 starters:
spring-boot-starter-oauth2-resource-serveradının Boot 4.0'daspring-boot-starter-security-oauth2-resource-serverolması. - RFC 9700: Best Current Practice for OAuth 2.0 Security: Ocak 2025, BCP 240; implicit için SHOULD NOT, password grant için MUST NOT, PKCE, refresh token koruması, tam redirect URI eşleşmesi.
- RFC 8725: JSON Web Token Best Current Practices: Algoritma doğrulaması,
none, RS256/HS256 karışıklığı,issveauddoğrulaması. - OAuth 2.1: Hâlâ taslak; PKCE zorunluluğu, implicit ve password grant'in çıkarılması.
- RFC 6749: The OAuth 2.0 Authorization Framework, RFC 7519: JSON Web Token, RFC 7636: PKCE: Temel standartlar; roller ve akışlar, JWT claim'leri,
code_verifierve S256. Bu oturumda sayfaları doğrudan açılamadı, ifadeler RFC 9700 ve RFC 8725'teki atıflarla tutarlı tutuldu. - OWASP Top 10:2025: Güncel kategori listesi. Release candidate duyurusu için: SD Times, 12 Kasım 2025.
- OWASP API Security Top 10 (2023): BOLA, BOPLA, SSRF (API7) ve diğer API riskleri.
- OWASP Password Storage Cheat Sheet: Argon2id, scrypt, bcrypt, PBKDF2 sırası ve minimum parametreleri, bcrypt'in 72 byte sınırı, pepper.