in

OWASP Top 10 ve Spring Boot: Uygulamalı Güvenlik Listesi

OWASP Top 10, kurumsal uygulamalarda en sık sömürülen zayıflıkların listesidir; 2025 sürümü tedarik zinciri ve hata yönetimini de ekledi. Her madde için Spring Boot'ta somut önlemi — nesne düzeyinde yetki kontrolü, güvenli yapılandırma ve başlıklar, bağımlılık taraması, parametreli sorgular, parola ve gizli bilgi yönetimi, güvenli loglama, kapalı başarısızlık — kod örnekleri ve CI kontrol listesiyle anlatıyoruz.

OWASP Top 10 maddelerini Spring Boot güvenlik önlemleriyle eşleyen kontrol listesi

OWASP Top 10, web uygulamalarında en sık sömürülen zayıflık sınıflarının listesidir ve kurumsal güvenlik denetimlerinin, sızma testlerinin ve müşteri güvenlik anketlerinin ortak dilidir. 2025 sürümü iki yeni başlık getirdi: yazılım tedarik zinciri hataları ve olağandışı durumların hatalı yönetimi. Liste "neyin yanlış gittiğini" söyler; "Spring Boot'ta bunu nasıl önlerim" sorusuna cevap vermez. Bu yazı o boşluğu dolduruyor.

Her madde için Spring Boot'ta somut önlemi, kod örneklerini ve CI'a konulacak kapıları veriyoruz. Yazı savunma odaklıdır: amaç uygulamanızı denetime ve saldırıya hazır hale getirmektir. KVKK uyumlu tasarım ve Spring Security + Keycloak yazıları bu listenin tamamlayıcısıdır.

OWASP Top 10 (2025) ve Spring Boot karşılıkları

#

Zayıflık sınıfı

Spring Boot'ta ana önlem

A01

Bozuk erişim kontrolü

Varsayılan reddet; metot güvenliği; nesne düzeyinde sahiplik/kiracı kontrolü (IDOR)

A02

Güvenlik yapılandırma hataları

Actuator ve hata ayrıntıları kapalı; güvenlik başlıkları; CORS dar; profil bazlı sertleştirme

A03

Yazılım tedarik zinciri hataları

Bağımlılık ve imaj taraması, SBOM, iç registry, yapıt imzalama, CI'da bilinen açık kapısı

A04

Kriptografik hatalar

Her yerde TLS; parolalar Argon2/bcrypt; gizli bilgiler gizli yönetiminde; alan bazlı şifreleme

A05

Enjeksiyon

Parametreli sorgular (JPA, JdbcClient); native SQL'de bağlama; komut ve SpEL enjeksiyonuna karşı girdi kısıtı; çıktı kodlama

A06

Güvensiz tasarım

Tehdit modelleme; iş akışı kötüye kullanım senaryoları; hız sınırı; sınırda doğrulama

A07

Kimlik doğrulama hataları

Merkezi IdP (OIDC), MFA, kısa token, oturum yönetimi, brute-force koruması

A08

Yazılım ve veri bütünlüğü hataları

Güvenli deserialization (ObjectInputFilter, Jackson polimorfik tip kapalı), imzalı yapıt ve dağıtım hattı

A09

Güvenlik loglama ve uyarı hataları

Denetim olayları; kişisel veri ve gizli bilgi maskeli; merkezi log ve uyarı kuralları

A10

Olağandışı durumların hatalı yönetimi

Kapalı başarısızlık (fail closed); global hata işleyici; zaman aşımı ve kaynak sınırı; kısmi başarısızlıkta tutarlılık

A01 — Erişim kontrolü: varsayılan reddet, nesneyi kontrol et

En sık bulunan açık, rol kontrolü yapılmış ama nesne kontrolü yapılmamış uç noktadır: GET /api/faturalar/4521 — kullanıcı "muhasebe" rolündedir, ama 4521 başka kiracının faturasıdır (IDOR). Rol yetkiyi açar; sahiplik ve kiracı kontrolü kapıyı kapatır.

http.authorizeHttpRequests(a -> a
    .requestMatchers("/actuator/health/**").permitAll()
    .requestMatchers("/api/admin/**").hasRole("YONETICI")
    .requestMatchers("/api/**").authenticated()
    .anyRequest().denyAll());                                   // listede olmayan her yol kapalı

@Service
public class FaturaServisi {
    @PreAuthorize("hasRole('MUHASEBE')")
    @PostAuthorize("returnObject.kiraciId == authentication.token.claims['tenant_id']")   // nesne düzeyi: başka kiracının verisi dönmez
    public FaturaDto getir(long id) { return repo.dto(id).orElseThrow(FaturaBulunamadi::new); }
}

Ek olarak: kiracı filtresi veritabanında da zorunlu (bkz. RLS); yetki testleri her sürümde (A kullanıcısı B'nin kaydına 403 alır); yönetim uçları ayrı ağda; CORS yalnızca bilinen origin'ler.

A02 — Yapılandırma: üretimde neyin açık olduğunu bilin

management:
  endpoints:
    web:
      exposure:
        include: health, prometheus                 # env, beans, heapdump, threaddump dışarı açılmaz
  endpoint:
    health:
      show-details: when-authorized
server:
  error:
    include-stacktrace: never                       # yığın izi istemciye gitmez
    include-message: never
spring:
  jackson:
    deserialization:
      fail-on-unknown-properties: true             # beklenmeyen alanlar sessizce kabul edilmez
  h2:
    console:
      enabled: false
http.headers(h -> h
    .contentSecurityPolicy(c -> c.policyDirectives("default-src 'self'; frame-ancestors 'none'; object-src 'none'"))
    .httpStrictTransportSecurity(s -> s.includeSubDomains(true).maxAgeInSeconds(31536000))
    .frameOptions(HeadersConfigurer.FrameOptionsConfig::deny)
    .referrerPolicy(r -> r.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)));

Kural: üretim profili sertleştirilmiş varsayılanlarla başlar; geliştirme kolaylıkları (H2 konsolu, ayrıntılı hata, geniş CORS) yalnızca dev profilinde açılır ve CI, üretim profilinde bu ayarların kapalı olduğunu test eder.

A03 — Tedarik zinciri: bağımlılıklar da kodunuzdur

  • Bilinen açık taraması CI'da zorunlu: OWASP Dependency-Check ya da Trivy/Grype; CVSS eşiği (örn. 7+) üzeri açık derlemeyi kırar; imaj taraması konteyner için aynı.

  • İç registry ve sabitleme: Bağımlılıklar iç Maven proxy'sinden; sürümler kilitli; -SNAPSHOT ve sürüm aralığı üretimde yok. Yeni bağımlılık ekleme bir inceleme adımıdır (kaynak, bakım durumu, yazar).

  • SBOM: CycloneDX Maven eklentisi her sürümde SBOM üretir; müşteri güvenlik anketleri ve olay anında "bu kütüphaneyi kullanıyor muyuz" sorusu saniyede cevaplanır.

  • Yapıt bütünlüğü: CI imzalı yapıt üretir; dağıtım yalnızca imzalı imajı kabul eder; CI çalıştırıcılarının (runner) kendisi de güncel ve dar yetkili.

  • Var olmayan paket tuzağı: AI asistanların uydurduğu paket adları için iç registry ve inceleme tek koruma (bkz. AI kod asistanı).

A04 — Kriptografi: doğru ilkel, doğru yer

@Bean
PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();   // varsayılan bcrypt; {argon2} ile Argon2id'ye geçilebilir, eski hash'ler okunmaya devam eder
}

Parolalar yalnızca yavaş ve tuzlu hash ile (bcrypt/Argon2id) — SHA-256 parola için uygun değildir; ideali parolanın hiç uygulamaya gelmemesidir (merkezi IdP). Aktarımda her yerde TLS 1.2+; iç servisler arası da dahil. Gizli bilgiler kodda ve imajda değil, gizli yönetiminde (Vault, bulut KMS, Kubernetes Secret + External Secrets). Kişisel ve finansal alanlarda uygulama katmanı şifreleme (AES-GCM, KMS ile yönetilen anahtar, anahtar döndürme); kendi şifreleme algoritmanızı yazmayın, JDK ve Tink gibi kütüphaneleri kullanın. Rastgelelik için SecureRandom; token ve kodlar için en az 128 bit entropi.

A05 — Enjeksiyon: veri hiçbir zaman kod olmaz

// YANLIŞ: string birleştirme
jdbc.sql("SELECT * FROM musteri WHERE ad = '" + ad + "'");

// DOĞRU: bağlama parametresi (JPA, JdbcClient, native query fark etmez)
jdbc.sql("SELECT id, ad FROM musteri WHERE ad = :ad AND kiraci_id = :kiraci").param("ad", ad).param("kiraci", kiraci).query(MusteriDto.class).list();

// Sıralama sütunu gibi parametrelenemeyen kısımlar: izin listesi
Set<String> siralanabilir = Set.of("ad", "tarih", "tutar");
if (!siralanabilir.contains(sort)) throw new GecersizIstek("sort");

Diğer enjeksiyon yüzeyleri: işletim sistemi komutu (ProcessBuilder'a kullanıcı girdisi girmez), LDAP filtreleri, SpEL ifadelerinin kullanıcı girdisiyle kurulması (asla), XML ayrıştırıcıda dış varlık (XXE: DocumentBuilderFactory'de disallow-doctype-decl), şablon motorlarında ham çıktı (Thymeleaf th:utext yalnızca güvenilir içerik), log enjeksiyonu (satır sonu karakterleri temizlenir). Bean Validation ile sınırda uzunluk ve biçim kısıtı, enjeksiyonu önlemez ama saldırı yüzeyini küçültür.

A06 — Güvensiz tasarım: kod doğru, akış yanlış

Doğru yazılmış kodla kötüye kullanılabilen iş akışları: sınırsız "şifremi unuttum" denemesi, kupon kodunu binlerce kez deneme, iade akışında miktar doğrulamasının eksikliği, fiyatın istemciden gelmesi. Önlem tasarım aşamasındadır: her özellik için "bunu nasıl kötüye kullanırdım" sorusu (hafif tehdit modelleme), kritik uçlarda hız sınırı ve kullanıcı/IP bazlı kota (Bucket4j ya da geçit katmanı), iş kurallarının sunucuda ve domain modelinde doğrulanması (fiyat, miktar, durum geçişleri), kritik işlemlerde ikinci kanal onayı.

A07 — Kimlik doğrulama

Girişi kendiniz yazmayın: merkezi IdP, OIDC, MFA, kısa ömürlü access token, döndürmeli refresh token, brute-force ve credential-stuffing koruması, oturum sabitleme koruması (Spring Security varsayılan), çıkışta oturum ve token iptali. Ayrıntılar Keycloak yazısında.

A08 — Bütünlük: neyi deserialize ediyorsunuz?

// Java serileştirme kullanılıyorsa (kullanmayın; ama eski sistemlerde kalır) filtre zorunlu
ObjectInputFilter.Config.setSerialFilter(ObjectInputFilter.Config.createFilter("com.acme.model.*;java.base/*;!*"));

// Jackson: polimorfik tip bilgisi kapalı kalır
// ObjectMapper.enableDefaultTyping() / @JsonTypeInfo(use = Id.CLASS) kullanılmaz

Ayrıca: yapıt ve imaj imzası (CI'da), dağıtım hattının kendisinin korunması (branch protection, inceleme zorunluluğu, CI gizli bilgilerinin dar kapsamı), üçüncü taraf script'lerin SRI ile yüklenmesi, yazılım güncelleme mekanizmalarının imza doğrulaması.

A09 — Loglama ve uyarı: olayı görebilmek

@Component
public class DenetimLoglayici {
    @EventListener
    public void basarisizGiris(AbstractAuthenticationFailureEvent e) {
        log.warn("audit=login_failed user={} ip={}", Maske.kullanici(e.getAuthentication().getName()), istekBaglami.ip());
    }
    @EventListener
    public void yetkiReddi(AuthorizationDeniedEvent<?> e) {
        log.warn("audit=access_denied user={} resource={}", e.getAuthentication().get().getName(), e.getAuthorizationResult());
    }
}

Loglanması gerekenler: giriş ve başarısız giriş, yetki reddi, kritik veri değişiklikleri (kim, ne, ne zaman), yönetici işlemleri, entegrasyon hataları. Loglanmaması gerekenler: parola, token, kart numarası, kişisel verinin tamamı (maskeleme). Loglar merkezi ve değiştirilemez; uyarı kuralları: aynı IP'den dakikada N başarısız giriş, ani yetki reddi artışı, saat dışı yönetici işlemi. Log olmadan olay "ne oldu" sorusuz kapanır; uyarı olmadan log yalnızca arkeoloji malzemesidir.

A10 — Olağandışı durumlar: hata anında ne olur?

@RestControllerAdvice
public class GlobalHataIsleyici {
    @ExceptionHandler(Exception.class)
    ProblemDetail beklenmeyen(Exception e, HttpServletRequest req) {
        String olayId = UUID.randomUUID().toString();
        log.error("olayId={} path={}", olayId, req.getRequestURI(), e);        // ayrıntı logda
        var p = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR);
        p.setDetail("Beklenmeyen hata. Destek kodu: " + olayId);              // istemciye yalnızca korelasyon kodu
        return p;
    }
}

İlkeler: kapalı başarısızlık — yetki servisi cevap vermiyorsa erişim reddedilir, izin verilmez; hata mesajları iç yapıyı sızdırmaz (yığın izi, SQL, dosya yolu); her dış çağrıda zaman aşımı ve devre kesici (bkz. Resilience4j); istek gövdesi, dosya boyutu ve sayfa boyutu sınırlı; kısmi başarısızlıkta tutarlılık (transaction, outbox) ve idempotent yeniden deneme. "Hata olunca ne olur" sorusu, her uç nokta için tasarım incelemesinin parçasıdır.

CI kontrol listesi

  • Gizli bilgi taraması (gitleaks/trufflehog) her commit'te; bulunursa derleme kırılır.

  • SAST (SpotBugs + Find Security Bugs, Semgrep ya da ticari araç) her PR'da; yüksek bulgular birleştirmeyi engeller.

  • Bağımlılık ve imaj taraması; CVSS eşiği; SBOM üretimi.

  • Güvenlik yapılandırma testi: üretim profiliyle açılan uygulamada actuator, hata ayrıntısı, CORS ve başlıklar için otomatik test.

  • Yetki testleri: her kaynak için "başka kullanıcı/kiracı 403 alır" testi.

  • DAST (OWASP ZAP) staging'de zamanlanmış; sızma testi yılda en az bir kez ve büyük sürümlerde.

  • Güvenlik yamalarının uygulanma süresi (kritik CVE için 7 gün gibi) izlenen bir metrik.

Sahada sık yapılan hatalar

  1. Rol var, sahiplik yok: IDOR, sızma testlerinde birinci bulgu.

  2. Actuator'ın env ve heapdump'ı açık: Gizli bilgiler tek istekle.

  3. Bağımlılık taraması "uyarı" modunda: Kimse okumaz; kapı olmalı.

  4. Parola için SHA-256: Sızan tablo saatler içinde çözülür.

  5. Sıralama/filtre alanında string birleştirme: "Parametreli sorgu kullanıyoruz" cümlesinin istisnası.

  6. Token ve kişisel veri logda: Log sistemi ikinci veri sızıntısı yüzeyi.

  7. Açık başarısızlık: Yetki servisi düşünce "geçici olarak izin ver".

Sık sorulan sorular

OWASP Top 10 uyumu bir sertifika mı?

Hayır; bir farkındalık ve öncelik listesidir. Denetimlerde ve müşteri anketlerinde referans olarak kullanılır; kapsamlı doğrulama için OWASP ASVS (Application Security Verification Standard) seviyeleri kullanılır. Bu yazıdaki kontroller ASVS seviye 1–2'nin büyük bölümünü karşılar.

Spring Boot varsayılanları yeterli mi?

Spring Security'nin varsayılanları iyidir (CSRF, oturum sabitleme, başlıkların bir kısmı); ama actuator açığı, hata ayrıntısı, CORS ve nesne düzeyi yetki gibi kararlar uygulamaya aittir. "Spring Security ekledik" cümlesi güvenlik değil, başlangıçtır.

Küçük ekipte nereden başlamalı?

Üç adım: CI'a gizli bilgi + bağımlılık taraması kapısı (bir gün), üretim profilinde yapılandırma sertleştirme (bir gün), her kaynağa yetki testi (sürekli). Sonra loglama ve uyarı. Bu dört adım, sızma testlerindeki bulguların çoğunu önceden kapatır.

Sonuç

OWASP Top 10 bir korku listesi değil, bir iş listesidir: varsayılan reddeden ve nesneyi kontrol eden yetki, sertleştirilmiş üretim yapılandırması, kapı olarak çalışan bağımlılık taraması, doğru kriptografik ilkeller, her yerde parametreli sorgu, kötüye kullanım senaryolarıyla tasarım, merkezi kimlik doğrulama, güvenli deserialization, maskeli ve uyarılı loglama, kapalı başarısızlık. Her biri Spring Boot'ta birkaç satır ya da bir CI adımıdır; toplamı denetime hazır bir uygulamadır.

Uygulamalarınızın güvenlik değerlendirmesi, CI güvenlik kapılarının kurulumu ya da sızma testi bulgularının kapatılması için bakım ve destek ile kurumsal uygulama geliştirme hizmetlerimize göz atabilirsiniz.