Stronger_Cracker
- 25 Tem 2022
- 6,408 Mesaj
Aktiflik
Seviye
Deneyim
Java ekosisteminde yıllardır projeler geliştiriyorum, kurumsal tarafta da epey acı hikaye gördüm; işin ilginci, çoğu yerde “Java zaten güvenli, managed language” gibi bir rahatlık var. Evet, Java bellek yönetimi konusunda C/C++’a göre avantajlı ama OWASP Top 10’da gördüğümüz problemlerin çok büyük kısmı doğrudan Java katmanında üretiliyor. Yani mesele dilin kendisinden çok, geliştiricinin alışkanlıkları ve kullandığı framework’lerin nasıl konfigüre edildiği ile ilgili.
Özellikle Spring, Jakarta EE, JSF gibi framework’lerde “hızlıca feature çıkarma” baskısı yüzünden, güvenlik katmanı ya kopyala-yapıştır kodlarla geçiştiriliyor ya da tamamen “zaten firewall var” diyerek arka plana itiliyor. Benim gördüğüm en büyük hata, güvenliği mimari seviyede tasarlamak yerine, en sona bırakılan bir “checklist” işi gibi ele almak. Halbuki OWASP standartları tam tersini söylüyor: güvenlik, daha tas
arım aşamasında başlar ve kodun her katmanına yayılır.
Bir de şu var: Güvenli kodlama dendiğinde herkesin aklına sadece XSS ve SQL Injection geliyor. Oysa OWASP güncel listelerinde kimlik doğrulama zafiyetleri, yetkilendirme hataları, güvensiz deserializasyon ve güvenlik loglama ve monitoring eksiklikleri gibi konular en az XSS kadar kritik. Java tarafında özellikle deserializasyon ve access control hataları, “nasıl olsa framework hallediyor” diye geçilen yerlerde patlıyor.
OWASP dokümanlarını okuyan çok, ama bunları Java projelerine doğrudan nasıl map edeceğini bilen az. Örneğin OWASP Top 10 2021 listesinde A01: Broken Access Control ilk sırada, fakat birçok Java projesinde hâlâ sadece UI katmanında buton gizleyerek “yetkilendirme yaptığını” düşünen ekipler görüyorum. Java tarafında gerçek yetkilendirme, method-level security, URL
tabanlı security ve domain seviyesinde yetki kontrolü ile birlikte kurgulanmalı.
A02: Cryptographic Failures tarafında, Java ekosistemi aslında güçlü; Java Cryptography Architecture (JCA), Java Cryptography Extension (JCE) gibi sağlam altyapılar var. Fakat geliştiriciler yanlış algoritma seçimi yapınca her şey çöp oluyor. Örneğin hâlâ MD5 veya SHA-1 ile parola hashleyen legacy kodlar görüyorum. OWASP burada çok net: SHA-256 bile tek başına yeterli değil; PBKDF2, bcrypt, scrypt, Argon2 gibi password hashing için tasarlanmış fonksiyonlar kullanmak gerekiyor.
A08: Software and Data Integrity Failures ise Java dünyasında genelde dependency management tarafında patlıyor. Maven veya Gradle ile onlarca kütüphaneyi çekiyoruz ama kimse OWASP Dependency-Check, OWASP Dependency-Track veya Snyk ile bu bağımlılıkların zafiyet durumunu taramıyor. Birkaç kez “bizim kodumuz temiz” denilen projelerde, sadece bağım
lılıklardan dolayı RCE açığına kadar giden zafiyetler gördüm.
SQL Injection konusu artık “101” gibi görülüyor ama Java tarafında özellikle JDBC ve JPA/Hibernate kullanırken hâlâ çok temel hatalar yapılıyor. Klasik örnek:
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
Statement st = connection.createStatement();
ResultSet rs = st.executeQuery(sql);
Bu tarz bir kodu prod’da hâlâ görmek istemezdim ama maalesef görenlerdenim. Burada yapılması gereken, OWASP’in de defalarca vurguladığı gibi parametrik sorgu kullanmak ve PreparedStatement’tan şaşmamak. Örneğin:
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, username);
JPA/Hibernate tarafında da aynı hikay
e geçerli. Query q = em.createQuery("SELECT u FROM User u WHERE u.name = '" + name + "'"); gibi string birleştirme ile HQL/JPQL yazmak, Injection’a davetiye çıkarıyor. Bunun yerine Query q = em.createQuery("SELECT u FROM User u WHERE u.name = :name"); ve q.setParameter("name", name); şeklinde parametreli yaklaşım kullanmak gerekiyor. Spring Data JPA kullanıyorsanız, zaten metot isimlendirme ile gelen sorgularda injection riski oldukça düşüyor, yeter ki “native query” yazarken parametrik sorgudan ödün vermeyin.
XSS tarafında Java geliştiricilerinin en sık düştüğü tuzak, “biz backend’ciyiz, XSS front-end’in problemi” düşüncesi. OWASP burada çok net: output encoding backend’in sorumluluğudur ve Java stack içinde bu işi doğru yapan bileşenler var. Örneğin Thymeleaf, JSP, JSF gibi teknolojilerde, default escaping davranışını bilmek
ve bozmamak kritik.
Thymeleaf kullanırken th:text="${userInput}" çıktıyı otomatik encode ederken, th:utext kullandığınızda HTML’i raw basıyor ve XSS riskini direkt olarak üzerinize alıyorsunuz. Benzer şekilde JSP’de kullanmak yerine, veya ${fn:escapeXml(userInput)} tercih edilmeli. OWASP’in Java Encoder Project kütüphanesi (owasp-java-encoder), özellikle JSP/Servlet tabanlı projelerde güvenli encoding için güzel bir araç.
Bir diğer sık yapılan hata da, React/Vue/Angular ile yazılmış front-end’e JSON gönderen REST API’lerde “XSS olamaz” diye düşünmek. Eğer API’nizden dönen veriler daha sonra başka bir context’te (örneğin bir legacy JSP sayfasında) kullanılıyorsa, backend’de en azından temel output encoding ve HTML içinde kullanılmayacak veri modeli tasarımı gibi prensipleri gözetmek gerekiyor. Ayrıca Content-Security-Policy (CSP) header’larını Java
tarafında Filter veya Spring Security konfigürasyonu ile ayarlamak, XSS’nin etkisini ciddi ölçüde azaltıyor.
Kurumsal Java projelerinde en çok karşıma çıkan güvenlik katmanı hâlâ Spring Security. Güçlü bir framework ama yanlış konfigüre edildiğinde OWASP Top 10’daki birçok maddeyi bizzat uygulamış oluyorsunuz. Örneğin Broken Authentication kısmında, zayıf parola politikaları, çok denemeli login brute force’ları ve JWT’lerin yanlış kullanımı sık gördüğüm problemler arasında.
Parola tarafında OWASP, minimum uzunluk, karmaşıklık ve rate limiting gibi konularda net tavsiyeler veriyor. Spring Security ile PasswordEncoder olarak hâlâ NoOpPasswordEncoder veya plain MessageDigestPasswordEncoder kullananlar çıkıyor. Burada en azından BCryptPasswordEncoder kullanmak, mümkünse parametrelerini (strength
) OWASP Password Storage Cheat Sheet’te önerilen değerlere göre ayarlamak gerekiyor. Örneğin: new BCryptPasswordEncoder(12) gibi bir cost değeriyle başlamak çoğu sistem için makul.
JWT tarafında da iki büyük hata görüyorum: expiry süresinin aşırı uzun tutulması ve imza anahtarının zayıf veya sızdırılmış olması. OWASP’in tavsiyesi, access token’ları kısa ömürlü tutmak, refresh token’ları daha sıkı saklamak ve mümkünse token revocation mekanizması kullanmak yönünde. Spring Security 5+ ile OAuth2ResourceServer ve JwtDecoder doğru konfigüre edilirse, imza algoritmalarının (örn. HS256 yerine RS256/ES256) güvenli seçimi ve anahtar yönetimi nispeten daha kontrol edilebilir hale geliyor.
Java dünyasında güvensiz deserializasyon yıllardır gündemde ve hâlâ birçok projede bu konuya gereken önem verilmiyor. Özellikle ObjectInputStream
ile dış kaynaktan gelen byte[] veriyi doğrudan deserialize eden kodlar, OWASP’in en sevdiği RCE demo’larından. Benim bizzat gördüğüm vakalarda, eski bir kütüphanenin readObject zinciri üzerinden saldırganın keyfi komut çalıştırabildiği senaryolar vardı.
Burada en temel kural şu: asla güvenilmeyen kaynaktan gelen veriyi doğrudan Java native serialization ile açma. Mümkünse Java native serialization’ı tamamen kapatıp, JSON (Jackson, Gson) veya Protobuf, Avro gibi daha kontrollü formatlara geçmek gerekiyor. Jackson kullanıyorsanız bile enableDefaultTyping gibi özellikleri kafaya göre açmamak, allow list mantığıyla hangi tiplerin deserialize edilebileceğini kısıtlamak önemli.
Java 9 ve sonrası, ObjectInputFilter gibi mekanizmalarla sınıf seviyesinde filtreleme yapmayı sağlıyor. Örneğin: ObjectInputFilter.Config.createFilter("maxbytes=1024;!com.example.malicious.*;") gibi bir filtre ile belirli paketleri bloklay
abilirsiniz. OWASP’in önerisi, mümkün olduğunca native serialization’ı terk etmek, legacy kalması gereken yerlerde ise whitelist temelli type filtering ve sıkı input validation uygulamak yönünde.
Birçok Java projesinde güvenlik denince akla sadece önüne auth koymak geliyor, ama olay olduktan sonra ne olduğunu anlamak için ihtiyacımız olan şey güvenli loglama ve audit trail. OWASP, Security Logging and Monitoring Failures başlığı altında, özellikle güvenlik olaylarının doğru seviyede ve yeterli detayla loglanmaması problemine dikkat çekiyor. Slf4j + Logback/Log4j2 kullanan projelerde, log formatı ve içerik tasarımı genelde “default” bırakılmış oluyor.
Burada yapılan hatalardan biri, loglarda kişisel veri, parola veya token gibi hassas bilgileri direkt yazmak. Java tarafında MDC kullanarak her isteğe bir
correlationId eklemek, hataları ve güvenlik olaylarını sonradan takip etmeyi ciddi anlamda kolaylaştırıyor. Ayrıca login denemeleri, başarısız authorization girişimleri, şüpheli IP’lerden gelen istekler gibi olayları ayrı bir security log kanalına basmak, SIEM (Splunk, ELK, Graylog) entegrasyonunda çok işe yarıyor.
Ben birkaç projede, Spring AOP ile kritik servis metodlarına audit aspect yazarak, kim hangi kaynağa ne zaman erişti, hangi alanları değiştirdi gibi bilgileri ayrı bir audit tablosuna attım. OWASP’in önerdiği yaklaşım da bu yönde: sadece “hata oldu” loglamak değil, güvenlik bağlamı olan aksiyonları da takip etmek. Tabii tüm bunları yaparken, logların kendisinin de yetkisiz erişime karşı korunması (file permission’lar, merkezi log sunucuları, immutable log storage gibi) gerektiğini atlamamak gerekiyor.
Güvenli Java geliştirme işini sadece “geliştiri
cinin dikkati”ne bırakırsanız, bir noktada mutlaka hata kaçıyor. Benim başarılı bulduğum ekiplerin ortak noktası, OWASP prensiplerini CI/CD pipeline’larına gömmeleri. Maven/Gradle projelerinde OWASP Dependency-Check plug-in’lerini kullanarak her build’de 3rd-party kütüphaneleri taramak, en temel adımlardan biri. Buna ek olarak SpotBugs + Find Security Bugs, SonarQube gibi SAST araçlarıyla statik kod analizi yapmak ciddi fark yaratıyor.
Örneğin Jenkins veya GitLab CI’da, mvn org.owasp:dependency-check-maven:check gibi bir adımı zorunlu hale getirip, kritik zafiyet bulunduğunda build’i fail ettirmek, OWASP’ın “early detection” yaklaşımıyla birebir örtüşüyor. Aynı şekilde, Docker image’leriniz varsa Trivy, Grype gibi container vulnerability scanner’ları da pipeline’a eklenmeli. Java projesinin kendisi ne kadar temiz olursa olsun, üzerinde koştuğu imaj zafiyetliyse resim tamamlanmıyor.
Dinamik tarafta ise, staging ortamında çalışan Java uygulamasına karşı
OWASP ZAP veya Burp Suite ile otomatik taramalar yapmak güzel sonuç veriyor. Özellikle ZAP’ı headless modda CI’ya entegre edip, her release öncesi temel OWASP Top 10 testlerini koşturmak mümkün. Böylece XSS, SQLi, açık HTTP header’ları, zayıf TLS konfigürasyonu gibi şeyler prod’a çıkmadan yakalanabiliyor.
Java ile güvenli kodlama tarafında gerçekten mesafe almak isteyen ekipler için bence en mantıklı yaklaşım, önce küçük ama somut bir “minimum güvenlik standardı” belirlemek. Örneğin “tüm DB erişimleri parametrik olacak, hiç bir yerde string concat yok”, “parola saklama sadece BCrypt/Argon2 ile yapılacak”, “hiçbir yerde ObjectInputStream ile dış veri deserialize edilmeyecek” gibi çok net ve ölçülebilir kurallar koymak lazım. Bunları code review checklist’lerine gömmek ve PR sürecinde tartışılır hale getirmek, kültürü de değiştiriyor.
İkinci adım, ekibin OWASP dokümanlarına aş
ina olmasını sağlamak. Özellikle OWASP Cheat Sheets (Java, Authentication, Session Management, Password Storage, XSS Prevention, SQL Injection Prevention vs.) kısa ama çok pratik dokümanlar. Ben yeni başlayanlara her zaman önce bu cheat sheet’leri verip, mevcut kod tabanına “bunlara göre nerede patlıyoruz” diye baktırıyorum. Geliştirici kendi kodunda problemi görünce, konu hemen teoriden pratiğe iniyor.
Son olarak, güvenli kodlamayı bir “ekstra iş yükü” değil, kalite metriği olarak görmek gerekiyor. Java dünyasında performans, ölçeklenebilirlik, clean code ne kadar konuşuluyorsa; OWASP uyumu, güvenli konfigürasyon, minimal attack surface konuları da o kadar konuşulmalı. Bence iyi bir Java geliştiriciyi artık sadece “Spring biliyor mu, Hibernate biliyor mu?” değil; “OWASP Top 10’u kendi koduna nasıl uygular, hangi araçları kullanır, pipeline’a güvenliği nasıl entegre eder?” sorularına verdiği cevap belirliyor. Bu bakış açısı oturduğunda, Java ile güvenli geliştirme sandığımız kadar zor olmuyor
