CSRF ve Güvenlik Header'ları
Web güvenliğinin önemli bir bölümü, çeşitli saldırı vektörlerine karşı koruma sağlayan mekanizmalardır. Spring Security, CSRF koruması ve güvenlik header'ları ile yaygın saldırıları otomatik olarak engeller. Bu derste bu korumaların nasıl çalıştığını ve nasıl yapılandırıldığını öğreneceğiz.
CSRF (Cross-Site Request Forgery) Nedir?
CSRF, saldırganın kurbanın oturumunu kullanarak yetkisiz istekler göndermesidir. Kurban farkında bile olmadan saldırganın hazırladığı bir sayfa aracılığıyla banka transferi yapabilir veya şifresini değiştirebilir:
Saldırı senaryosu:
1. Kullanıcı bank.com'a giriş yapmış (oturumu açık)
2. Kullanıcı evil.com'u ziyaret eder
3. evil.com'da gizli bir form var:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="saldırgan"/>
<input type="hidden" name="amount" value="10000"/>
</form>
<script>document.forms[0].submit();</script>
4. Tarayıcı bank.com'un cookie'sini otomatik gönderir
5. Bank.com isteği meşru sanır ve parayı transfer eder!CSRF Token Koruması
Spring Security, her form'a rastgele bir CSRF token ekler. Sunucu, gelen istekteki token'ı session'daki token ile karşılaştırır. Saldırgan bu token'ı bilmediği için sahte istek başarısız olur:
// Spring Security CSRF varsayılan olarak aktiftir
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf
// Varsayılan: HttpSessionCsrfTokenRepository
// Cookie tabanlı (SPA'lar için):
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// Belirli endpoint'leri CSRF'den muaf tutma
.ignoringRequestMatchers("/api/webhooks/**", "/api/public/**")
)
.build();
}Thymeleaf ile CSRF token otomatik eklenir:
<form th:action="@{/transfer}" method="post">
<!-- Thymeleaf otomatik olarak şunu ekler: -->
<!-- <input type="hidden" name="_csrf" value="random-token-here"/> -->
<input type="text" name="to"/>
<input type="number" name="amount"/>
<button type="submit">Transfer</button>
</form>SPA (React/Angular) ile CSRF:
// Cookie tabanlı CSRF token (JavaScript'in okuyabilmesi için)
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
)JavaScript tarafında:
// Cookie'den CSRF token'ı oku
const csrfToken = document.cookie
.split('; ')
.find(row => row.startsWith('XSRF-TOKEN='))
?.split('=')[1];
// Her istekte header olarak gönder
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-XSRF-TOKEN': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ to: 'Ali', amount: 100 })
});CSRF'yi ne zaman devre dışı bırakılır?
Stateless REST API'lerde (JWT authentication) CSRF koruması genellikle gereksizdir çünkü tarayıcı otomatik cookie göndermez — token header'da gönderilir. Ancak session-based authentication kullanıyorsanız CSRF mutlaka aktif olmalıdır:
// Sadece stateless API için!
http.csrf(csrf -> csrf.disable())
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));SameSite Cookie Attribute
SameSite, tarayıcının cross-site isteklerde cookie gönderip göndermeyeceğini kontrol eder. CSRF'ye karşı ek bir koruma katmanıdır:
# application.properties
server.servlet.session.cookie.same-site=LaxStrict: Cookie sadece aynı site'den gelen isteklerde gönderilir. En güvenli ama kullanıcı deneyimini bozabilir (harici linkten gelince oturum kaybı).
Lax (varsayılan): GET gibi güvenli HTTP metotlarında cross-site cookie gönderilir, POST gibi state-changing metotlarda gönderilmez. İyi bir denge.
None: Cookie her zaman gönderilir (Secure flag zorunlu). 3rd-party entegrasyonlar için.
HTTP Güvenlik Header'ları
Spring Security, varsayılan olarak birçok güvenlik header'ı ekler:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.headers(headers -> headers
// Content-Security-Policy
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; " +
"script-src 'self' 'nonce-{random}'; " +
"style-src 'self' https://fonts.googleapis.com; " +
"img-src 'self' data: https:; " +
"font-src 'self' https://fonts.gstatic.com")
)
// HSTS (HTTP Strict Transport Security)
.httpStrictTransportSecurity(hsts -> hsts
.includeSubDomains(true)
.maxAgeInSeconds(31536000) // 1 yıl
)
// X-Frame-Options
.frameOptions(frame -> frame.deny())
// X-Content-Type-Options
.contentTypeOptions(Customizer.withDefaults()) // nosniff
// X-XSS-Protection (legacy ama hâlâ yararlı)
.xssProtection(xss -> xss.headerValue(
XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK))
// Referrer-Policy
.referrerPolicy(ref -> ref
.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN))
// Permissions-Policy
.permissionsPolicy(perm -> perm
.policy("camera=(), microphone=(), geolocation=()"))
)
.build();
}Header'ların Açıklamaları
Content-Security-Policy (CSP): Sayfada hangi kaynaklardan script, style, image vb. yüklenebileceğini belirler. XSS saldırılarının en etkili savunmasıdır. default-src 'self' sadece kendi domain'inizden yüklemeye izin verir.
HSTS (HTTP Strict Transport Security): Tarayıcıya "bu siteye her zaman HTTPS ile bağlan" der. HTTP istekleri otomatik olarak HTTPS'e yönlendirilir. max-age=31536000 bir yıl boyunca geçerlidir.
X-Frame-Options: Sayfanın iframe içinde gösterilip gösterilmeyeceğini kontrol eder. DENY — hiçbir yerde iframe'lenemez. Clickjacking saldırısını önler. Saldırgan sayfanızı görünmez bir iframe'de gösterip üzerine sahte butonlar koyamaz.
X-Content-Type-Options: nosniff: Tarayıcının content-type'ı tahmin etmesini (MIME sniffing) engeller. Saldırganın bir JavaScript dosyasını image olarak yükleyip çalıştırmasını önler.
CORS (Cross-Origin Resource Sharing)
CORS, farklı origin'lerden gelen istekleri kontrol eder. SPA + API mimarilerinde frontend ve backend farklı origin'lerde çalışabilir:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://frontend.example.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-XSRF-TOKEN"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}Özet
CSRF koruması, session-based authentication'da zorunludur. SameSite cookie attribute'u ek koruma sağlar. Güvenlik header'ları (CSP, HSTS, X-Frame-Options) yaygın web saldırılarını engeller. Spring Security bu korumaların çoğunu varsayılan olarak aktif eder — geliştiricinin yapması gereken, bunları uygulamanın ihtiyaçlarına göre ince ayar yapmaktır.
AI Asistan
Sorularını yanıtlamaya hazır