Güvenlik Temelleri
Web uygulaması geliştirirken en kritik konu güvenliktir. Bir e-ticaret sitesinde kullanıcı bilgileri çalınabilir, bir bankacılık uygulamasında yetkisiz para transferi yapılabilir, bir sağlık sisteminde hasta kayıtları sızdırılabilir. Spring Security, Java ekosisteminin en güçlü ve en kapsamlı güvenlik framework'üdür — Spring Boot ile birlikte gelen bu kütüphane, authentication, authorization, CSRF koruması, session yönetimi ve çok daha fazlasını sağlar.
Güvenliği bir binanın güvenlik sistemine benzetebilirsiniz. Binanın kapısında kimlik kontrolü (authentication), katlara erişim kartı kontrolü (authorization), yangın alarmları (güvenlik header'ları), güvenlik kameraları (logging ve auditing) ve acil çıkış prosedürleri (fail securely) vardır. Spring Security, tüm bu katmanları tek bir framework'te sunar.
Authentication vs Authorization
Güvenliğin iki temel kavramı vardır ve bunlar sıkça karıştırılır. Aradaki farkı net anlamak kritiktir:
Authentication (Kimlik Doğrulama)
"Sen kimsin?" sorusunun cevabıdır. Kullanıcının iddia ettiği kişi olduğunu kanıtlama sürecidir.
Günlük hayattan örnekler:
Bir binaya girerken kimlik kartınızı göstermek authentication'dır
Telefona parmak izi ile giriş yapmak authentication'dır
Bir web sitesine kullanıcı adı + şifre ile giriş yapmak authentication'dır
Bankada SMS doğrulaması yapmak ek authentication'dır (2FA)
Web dünyasında authentication yöntemleri:
Kullanıcı adı + Şifre (Form login, HTTP Basic)
Token tabanlı (JWT, OAuth2 Access Token)
Sertifika tabanlı (X.509 Client Certificate)
Biyometrik (Parmak izi, yüz tanıma — mobilde)
Çok faktörlü (MFA/2FA — şifre + SMS/TOTP)
Authorization (Yetkilendirme)
"Ne yapabilirsin?" sorusunun cevabıdır. Kimliği doğrulanmış kullanıcının hangi kaynaklara erişebileceğini belirleme sürecidir.
Günlük hayattan örnekler:
Binaya girdikten sonra hangi katlara çıkabileceğiniz authorization'dır
Şirket çalışanının hangi klasörlere erişebileceği authorization'dır
Bir film platformunda premium içeriklere erişim authorization'dır
Web'de authorization mekanizmaları:
Rol tabanlı (RBAC — Role-Based Access Control): ADMIN, USER, EDITOR rolleri
İzin tabanlı (Permission-based): READ, WRITE, DELETE izinleri
Attribute tabanlı (ABAC): Kullanıcı özellikleri + kaynak özellikleri + ortam koşulları
URL tabanlı: /admin/** sadece ADMIN rolüne
Sıralama Kritik
Önce authentication, sonra authorization. Kimliğini bilmediğiniz birine yetki veremezsiniz. Spring Security de bu sırayı izler — önce kullanıcıyı tanır, sonra ne yapabileceğini kontrol eder.
Kullanıcı İsteği → [Authentication Filter] → Kim bu?
↓
Kimlik Doğru mu?
/ \
Evet Hayır
↓ ↓
[Authorization] 401 Unauthorized
↓
Yetkisi Var mı?
/ \
Evet Hayır
↓ ↓
200 OK 403 ForbiddenBu akışı bir havaalanı kontrolüne benzetebilirsiniz:
Pasaport kontrolü (Authentication) — Kim olduğunuzu kanıtlarsınız
Vize kontrolü (Authorization) — O ülkeye girme yetkiniz var mı kontrol edilir
Pasaportunuz geçersizse → Geri dönersiniz (401)
Vizeniz yoksa → Geri dönersiniz (403)
İkisi de tamamsa → Ülkeye girersiniz (200)
HTTP Durum Kodları: 401 vs 403
Bu iki hata kodu sıkça karıştırılır. İsimleri bile kafa karıştırıcıdır:
401 Unauthorized: Aslında "Unauthenticated" anlamındadır (isim yanıltıcı!). Kullanıcı kimliğini kanıtlamamış. "Kim olduğunu bilmiyorum, giriş yap" der. Token yoksa, token geçersizse veya süresi dolmuşsa bu yanıtı alırsınız.
403 Forbidden: Kullanıcı kimliğini kanıtlamış ama yetkisi yok. "Seni tanıyorum ama bu kaynağa erişemezsin" der. Normal kullanıcının admin paneline erişmeye çalışması bu duruma örnektir.
// Spring Security'de bu kodlar otomatik döner:
// Authentication başarısız → 401
// Authorization başarısız (yetki yok) → 403
// Kaynak bulunamadı → 404
// Controller'da elle de dönebilirsiniz:
@GetMapping("/admin/reports")
public ResponseEntity<?> getAdminReport(Authentication auth) {
if (auth == null || !auth.isAuthenticated()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("Lütfen giriş yapın");
}
if (!auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body("Admin yetkisi gerekli");
}
return ResponseEntity.ok(reportService.getAdminReport());
}💡 İpucu: 401 aldığınızda → login olun. 403 aldığınızda → yetkinizi kontrol edin veya admin'den yetki isteyin. API tasarlarken bu iki kodu doğru kullanmak, istemci geliştiriciyi doğru yönlendirir.
OWASP Top 10: En Kritik Web Güvenlik Riskleri
OWASP (Open Web Application Security Project), web güvenliği konusunda dünya genelinde referans kabul edilen bir topluluktur. "OWASP Top 10" listesi, en yaygın ve en tehlikeli web güvenlik açıklarını sıralar. Spring Security bu risklerin çoğuna karşı yerleşik koruma sağlar.
A01: Broken Access Control (Kırık Erişim Kontrolü)
En yaygın güvenlik açığı. Kullanıcıların yetkileri dışındaki kaynaklara erişmesi.
// ❌ IDOR (Insecure Direct Object Reference) Açığı
@GetMapping("/api/users/{id}/profile")
public UserProfile getProfile(@PathVariable Long id) {
// Herhangi bir kullanıcı başkasının profilini görebilir!
return userService.getProfile(id);
}
// ✅ GÜVENLI — Kendi profiline erişim kontrolü
@GetMapping("/api/users/{id}/profile")
@PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')")
public UserProfile getProfile(@PathVariable Long id) {
return userService.getProfile(id);
}A02: Cryptographic Failures (Kriptografik Hatalar)
Şifrelerin düz metin saklanması, zayıf hash algoritmaları (MD5, SHA-1), HTTPS kullanmama.
// ❌ YANLIŞ — Düz metin şifre
user.setPassword("mySecret123"); // Veritabanında açık metin!
// ❌ YANLIŞ — Zayıf hash
user.setPassword(DigestUtils.md5Hex("mySecret123")); // MD5 saniyeler içinde kırılır
// ✅ DOĞRU — BCrypt
user.setPassword(passwordEncoder.encode("mySecret123")); // Güvenli hash + saltSpring Security'nin PasswordEncoder ailesi (BCrypt, Argon2) bu riski minimize eder.
A03: Injection (Enjeksiyon)
SQL Injection, NoSQL Injection, LDAP Injection. Kullanıcı girdisinin doğrudan sorguya eklenmesi.
// ❌ SQL INJECTION AÇIĞI
@Query(value = "SELECT * FROM users WHERE name = '" + name + "'", nativeQuery = true)
List<User> findByName(String name);
// name = "'; DROP TABLE users; --" → Felaket!
// ✅ GÜVENLI — Parameterized query
@Query("SELECT u FROM User u WHERE u.name = :name")
List<User> findByName(@Param("name") String name);
// ✅ GÜVENLI — Spring Data JPA method naming
List<User> findByName(String name); // Otomatik parameterizedA05: Security Misconfiguration
Varsayılan şifrelerin değiştirilmemesi, gereksiz servislerin açık kalması, hata mesajlarında hassas bilgi sızdırılması.
# ❌ YANLIŞ — Production'da debug açık
spring.jpa.show-sql=true
server.error.include-stacktrace=always
# ✅ DOĞRU — Production ayarları
spring.jpa.show-sql=false
server.error.include-stacktrace=never
server.error.include-message=never
management.endpoints.web.exposure.include=health,infoA07: Cross-Site Scripting (XSS)
Kötü niyetli JavaScript kodunun sayfaya enjekte edilmesi. Thymeleaf'in otomatik escaping'i ve Spring Security'nin Content-Security-Policy header'ı koruma sağlar.
A08: Cross-Site Request Forgery (CSRF)
Kullanıcının oturumunu kullanarak yetkisiz istek gönderme. Spring Security varsayılan olarak CSRF koruması uygular.
Spring Security'nin Temel Prensipleri
Spring Security'nin tasarım felsefesini anlamak, onu etkili kullanmanın ön koşuludur:
1. Secure by Default (Varsayılan Güvenli)
Spring Security classpath'e eklendiği anda tüm endpoint'ler korunur. Hiçbir şey yapılandırmasanız bile her istek authentication gerektirir. Bu "varsayılan güvenli" yaklaşım, geliştiricinin güvenliği "açmayı unutmasını" engeller.
Düşünce şekli: "Her şey kapalı, gerekeni açarım" — bu güvenliğin altın kuralıdır. Tersi ("her şey açık, gerekeni kapatırım") tehlikelidir çünkü bir endpoint'i kapatmayı unutmak büyük güvenlik açığı oluşturur.
2. Defense in Depth (Derinlemesine Savunma)
Tek bir güvenlik katmanına güvenmek yerine, birden fazla katman kullanılır:
[HTTPS/TLS] → Şifreli iletişim
↓
[Firewall/WAF] → Kötü niyetli trafik engelleme
↓
[CORS] → Origin kontrolü
↓
[CSRF Token] → Sahte istek koruması
↓
[Security Filter Chain] → Authentication + Authorization
↓
[Method-Level Security] → @PreAuthorize
↓
[Data-Level Security] → IDOR koruması
↓
[Encryption at Rest] → Veritabanında şifreli veriHer katman bağımsız bir savunma hattıdır. Bir katman aşılsa bile diğerleri koruma sağlar.
3. Fail Securely (Güvenli Başarısızlık)
Bir hata oluştuğunda sistem güvensiz bir duruma düşmemeli. Spring Security'de bir exception fırlatıldığında istek reddedilir — "şüphe halinde eri" yaklaşımı.
// Spring Security'nin yaklaşımı:
// Exception → İstek REDDEDİLİR (güvenli taraf)
// Asla: Exception → İstek KABUL EDİLİR (güvensiz taraf)
// Kendi kodunuzda da aynı prensibi uygulayın:
public boolean hasAccess(User user, Resource resource) {
try {
return accessControlService.checkAccess(user, resource);
} catch (Exception e) {
log.error("Access check failed", e);
return false; // Şüphe halinde ERİŞİM REDDET
}
}4. Principle of Least Privilege (En Az Yetki Prensibi)
Kullanıcılara ihtiyaç duydukları minimum yetkiyi verin. Bir blog editörü admin paneline erişmemeli, bir rapor okuyucusu rapor silme yetkisine sahip olmamalı.
Spring Boot'a Spring Security Ekleme
Spring Security'yi projeye eklemek tek bir dependency ile olur:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>Bu dependency eklendiğinde otomatik olarak şunlar gerçekleşir:
Tüm HTTP endpoint'leri authentication gerektirir
Varsayılan bir login sayfası oluşturulur (
/login)`user` adında bir kullanıcı ve konsolda rastgele bir şifre üretilir
CSRF koruması aktif olur
Session fixation koruması aktif olur
Security header'ları (X-Content-Type-Options, X-Frame-Options vb.) eklenir
Logout endpoint'i (
/logout) oluşturulur
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
...
Using generated security password: a1b2c3d4-e5f6-7890-abcd-ef1234567890Bu şifreyi konsoldan kopyalayıp /login sayfasında user / <şifre> ile giriş yapabilirsiniz.
Hemen test edelim:
# Authentication olmadan istek → 401
curl -i http://localhost:8080/api/users
# HTTP/1.1 401 Unauthorized
# HTTP Basic authentication ile → 200
curl -i -u user:a1b2c3d4-e5f6-7890-abcd-ef1234567890 http://localhost:8080/api/users
# HTTP/1.1 200 OK⚠️ Dikkat: Konsolda üretilen şifre sadece geliştirme amaçlıdır — uygulama her yeniden başlatıldığında değişir. Üretim ortamında kendi kullanıcı yönetiminizi yapılandırmanız gerekir.
application.properties ile Varsayılan Kullanıcı
Konsoldan şifre kopyalamak yerine sabit bir kullanıcı tanımlayabilirsiniz:
# Sadece geliştirme ortamı için!
spring.security.user.name=admin
spring.security.user.password=admin123
spring.security.user.roles=ADMIN,USERSpring Security'nin Modüler Yapısı
Spring Security çok geniş bir framework'tür ve modüler bir yapıya sahiptir. Her modül bağımsız bir güvenlik konusunu ele alır:
Spring Security
├── Core (Temel)
│ ├── Authentication — Kimlik doğrulama
│ ├── Authorization — Yetkilendirme
│ └── SecurityContext — Güvenlik bağlamı
│
├── Web (HTTP Güvenlik)
│ ├── Filter Chain — Filtre zinciri
│ ├── CSRF Protection — CSRF koruması
│ ├── CORS — Cross-origin yönetimi
│ ├── Session Management — Oturum yönetimi
│ └── Security Headers — Güvenlik header'ları
│
├── Config (Yapılandırma)
│ ├── HttpSecurity DSL — Akıcı yapılandırma API'si
│ ├── @EnableWebSecurity — Web güvenliğini etkinleştirme
│ └── @EnableMethodSecurity — Metot seviyesi güvenlik
│
├── UserDetails (Kullanıcı Yönetimi)
│ ├── UserDetailsService — Kullanıcı yükleme
│ ├── PasswordEncoder — Şifre hash'leme
│ └── InMemoryUserDetailsManager — Bellek tabanlı kullanıcı
│
└── OAuth2 / JWT (Token Tabanlı — ileri konu)
├── OAuth2 Client
├── OAuth2 Resource Server
└── JWT TokenKursumuzda bu modülleri adım adım işleyeceğiz:
Filter Chain Mimarisi — güvenlik filtrelerinin nasıl çalıştığı
SecurityFilterChain Yapılandırması — hangi endpoint'lerin korunacağı
UserDetailsService — kullanıcı bilgilerinin nereden geleceği
Password Encoding — şifrelerin güvenli saklanması
Veritabanı Authentication — JPA ile kullanıcı yönetimi
Session Yönetimi — oturum güvenliği
CSRF ve Güvenlik Header'ları — saldırı korumaları
Authentication Yöntemlerinin Karşılaştırması
Modern web uygulamalarında birden fazla authentication yöntemi kullanılabilir. Her birinin avantaj ve dezavantajları vardır:
Form-Based Authentication
En geleneksel yöntem. Kullanıcı, HTML form üzerinden kullanıcı adı ve şifre gönderir. Sunucu doğrulama yapar ve session oluşturur.
// Spring Security varsayılan olarak form login sunar
http.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error=true")
);Avantajları: Basit, anlaşılır, tarayıcı uyumlu, session tabanlı. Dezavantajları: Stateful (sunucu session tutar), ölçeklenmesi zor, mobil uyumlu değil.
HTTP Basic Authentication
Her istekte kullanıcı adı ve şifre Base64 ile encode edilip Authorization header'ında gönderilir.
http.httpBasic(Customizer.withDefaults());
// İstemci tarafında:
// Authorization: Basic YWRtaW46cGFzc3dvcmQ= (admin:password)Avantajları: Çok basit, REST API'ler için uygun, stateless. Dezavantajları: Her istekte şifre gönderilir (HTTPS şart), logout mekanizması yok.
Token-Based Authentication (JWT)
Kullanıcı bir kez giriş yapar, sunucu bir JWT token üretir. Sonraki isteklerde bu token Authorization: Bearer xxx header'ında gönderilir.
http.oauth2ResourceServer(oauth2 ->
oauth2.jwt(Customizer.withDefaults()));
// İstemci tarafında:
// Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Avantajları: Stateless, microservice uyumlu, mobil uyumlu, ölçeklenebilir. Dezavantajları: Token çalınırsa süresi dolana kadar geçerli, token revocation zor, karmaşık implementasyon.
OAuth2 / OpenID Connect
Google, GitHub, Facebook gibi harici identity provider'lar ile giriş yapma. "Login with Google" butonları bu mekanizmayı kullanır.
Avantajları: Şifre yönetimi sizde değil, güvenilir provider'lar, kullanıcı deneyimi iyi. Dezavantajları: Harici bağımlılık, karmaşık akış, privacy endişeleri.
| Yöntem | Stateless | Mobil Uyumlu | Karmaşıklık | Kullanım |
|---|---|---|---|---|
| Form Login | ❌ | ❌ | Düşük | Web uygulamaları (SSR) |
| HTTP Basic | ✅ | ✅ | Çok düşük | Basit API, test |
| JWT | ✅ | ✅ | Orta | SPA + API, microservice |
| OAuth2 | ✅ | ✅ | Yüksek | Sosyal login, enterprise |
Spring Security vs Alternatifleri
| Özellik | Spring Security | Apache Shiro | JAAS |
|---|---|---|---|
| Spring entegrasyonu | Mükemmel | İyi | Zayıf |
| Öğrenme eğrisi | Dik | Orta | Dik |
| Özellik seti | Çok geniş | Orta | Temel |
| Topluluk | Çok büyük | Orta | Küçük |
| OAuth2/JWT | Yerleşik | Ek modül | Yok |
| Method-level security | Evet | Evet | Sınırlı |
Spring Boot kullanıyorsanız, Spring Security doğal seçimdir — otomatik yapılandırma, test desteği ve ekosistem entegrasyonu rakipsizdir.
Özet
Authentication (kimlik doğrulama) "sen kimsin", Authorization (yetkilendirme) "ne yapabilirsin" sorusunu cevaplar. Sıralama: önce authentication, sonra authorization.
401 Unauthorized = kimliğini kanıtlamamış (unauthenticated), 403 Forbidden = kimlik doğru ama yetki yok.
OWASP Top 10, en kritik web güvenlik risklerini sıralar. Spring Security bu risklerin çoğuna karşı yerleşik koruma sunar.
Spring Security'nin prensipleri: Secure by Default (varsayılan güvenli), Defense in Depth (çok katmanlı savunma), Fail Securely (güvenli başarısızlık).
`spring-boot-starter-security` eklendiğinde tüm endpoint'ler otomatik korunur, login sayfası oluşturulur ve güvenlik header'ları eklenir.
Güvenlik bir süreçtir, tek seferlik bir iş değil. Düzenli güvenlik denetimleri, bağımlılık güncellemeleri ve OWASP kılavuzlarını takip etmek şarttır.
AI Asistan
Sorularını yanıtlamaya hazır